mise en prod app
This commit is contained in:
parent
fa1709216b
commit
2a20032b02
|
@ -8,6 +8,7 @@
|
||||||
<sourceFolder url="file://$MODULE_DIR$/nur_src" isTestSource="false" packagePrefix="nur\" />
|
<sourceFolder url="file://$MODULE_DIR$/nur_src" isTestSource="false" packagePrefix="nur\" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/nur_tests" isTestSource="true" packagePrefix="nur\" />
|
<sourceFolder url="file://$MODULE_DIR$/nur_tests" isTestSource="true" packagePrefix="nur\" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/wip" isTestSource="false" packagePrefix="nur\sery\wip\" />
|
<sourceFolder url="file://$MODULE_DIR$/wip" isTestSource="false" packagePrefix="nur\sery\wip\" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/wip_app" isTestSource="false" packagePrefix="nur\sery\app\" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/vendor" />
|
<excludeFolder url="file://$MODULE_DIR$/vendor" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
|
@ -49,8 +49,9 @@
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"nur\\sery\\": "src",
|
|
||||||
"nur\\sery\\wip\\": "wip",
|
"nur\\sery\\wip\\": "wip",
|
||||||
|
"nur\\sery\\app\\": "wip_app",
|
||||||
|
"nur\\sery\\": "src",
|
||||||
"nur\\": "nur_src"
|
"nur\\": "nur_src"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|
|
@ -75,20 +75,20 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.30.0",
|
"version": "v1.31.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1"
|
"php": ">=7.2"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"ext-ctype": "*"
|
"ext-ctype": "*"
|
||||||
|
@ -134,7 +134,7 @@
|
||||||
"portable"
|
"portable"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -150,20 +150,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-05-31T15:07:36+00:00"
|
"time": "2024-09-09T11:45:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/yaml",
|
"name": "symfony/yaml",
|
||||||
"version": "v5.4.40",
|
"version": "v5.4.44",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/yaml.git",
|
"url": "https://github.com/symfony/yaml.git",
|
||||||
"reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83"
|
"reference": "7025b964f123bbf1896d7563db6ec7f1f63e918a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/81cad0ceab3d61fe14fe941ff18a230ac9c80f83",
|
"url": "https://api.github.com/repos/symfony/yaml/zipball/7025b964f123bbf1896d7563db6ec7f1f63e918a",
|
||||||
"reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83",
|
"reference": "7025b964f123bbf1896d7563db6ec7f1f63e918a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -209,7 +209,7 @@
|
||||||
"description": "Loads and dumps YAML files",
|
"description": "Loads and dumps YAML files",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/yaml/tree/v5.4.40"
|
"source": "https://github.com/symfony/yaml/tree/v5.4.44"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -225,7 +225,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-05-31T14:33:22+00:00"
|
"time": "2024-09-16T14:36:56+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
|
@ -747,16 +747,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
"version": "v5.1.0",
|
"version": "v5.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
"reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1"
|
"reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1",
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3abf7425cd284141dc5d8d14a9ee444de3345d1a",
|
||||||
"reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1",
|
"reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -799,9 +799,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0"
|
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.0"
|
||||||
},
|
},
|
||||||
"time": "2024-07-01T20:03:41+00:00"
|
"time": "2024-09-29T13:56:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nulib/tests",
|
"name": "nulib/tests",
|
||||||
|
@ -955,16 +955,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpoffice/phpspreadsheet",
|
"name": "phpoffice/phpspreadsheet",
|
||||||
"version": "1.29.0",
|
"version": "1.29.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||||
"reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0"
|
"reference": "3a5a818d7d3e4b5bd2e56fb9de44dbded6eae07f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fde2ccf55eaef7e86021ff1acce26479160a0fa0",
|
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/3a5a818d7d3e4b5bd2e56fb9de44dbded6eae07f",
|
||||||
"reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0",
|
"reference": "3a5a818d7d3e4b5bd2e56fb9de44dbded6eae07f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -999,7 +999,7 @@
|
||||||
"phpcompatibility/php-compatibility": "^9.3",
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
"phpstan/phpstan": "^1.1",
|
"phpstan/phpstan": "^1.1",
|
||||||
"phpstan/phpstan-phpunit": "^1.0",
|
"phpstan/phpstan-phpunit": "^1.0",
|
||||||
"phpunit/phpunit": "^8.5 || ^9.0 || ^10.0",
|
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||||
"squizlabs/php_codesniffer": "^3.7",
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
"tecnickcom/tcpdf": "^6.5"
|
"tecnickcom/tcpdf": "^6.5"
|
||||||
},
|
},
|
||||||
|
@ -1054,41 +1054,41 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||||
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.0"
|
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.2"
|
||||||
},
|
},
|
||||||
"time": "2023-06-14T22:48:31+00:00"
|
"time": "2024-09-29T07:04:47+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.31",
|
"version": "9.2.32",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||||
"reference": "48c34b5d8d983006bd2adc2d0de92963b9155965"
|
"reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965",
|
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5",
|
||||||
"reference": "48c34b5d8d983006bd2adc2d0de92963b9155965",
|
"reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"nikic/php-parser": "^4.18 || ^5.0",
|
"nikic/php-parser": "^4.19.1 || ^5.1.0",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
"phpunit/php-file-iterator": "^3.0.3",
|
"phpunit/php-file-iterator": "^3.0.6",
|
||||||
"phpunit/php-text-template": "^2.0.2",
|
"phpunit/php-text-template": "^2.0.4",
|
||||||
"sebastian/code-unit-reverse-lookup": "^2.0.2",
|
"sebastian/code-unit-reverse-lookup": "^2.0.3",
|
||||||
"sebastian/complexity": "^2.0",
|
"sebastian/complexity": "^2.0.3",
|
||||||
"sebastian/environment": "^5.1.2",
|
"sebastian/environment": "^5.1.5",
|
||||||
"sebastian/lines-of-code": "^1.0.3",
|
"sebastian/lines-of-code": "^1.0.4",
|
||||||
"sebastian/version": "^3.0.1",
|
"sebastian/version": "^3.0.2",
|
||||||
"theseer/tokenizer": "^1.2.0"
|
"theseer/tokenizer": "^1.2.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.3"
|
"phpunit/phpunit": "^9.6"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-pcov": "PHP extension that provides line coverage",
|
"ext-pcov": "PHP extension that provides line coverage",
|
||||||
|
@ -1097,7 +1097,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "9.2-dev"
|
"dev-main": "9.2.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -1126,7 +1126,7 @@
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31"
|
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1134,7 +1134,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-03-02T06:37:42+00:00"
|
"time": "2024-08-22T04:23:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-file-iterator",
|
"name": "phpunit/php-file-iterator",
|
||||||
|
@ -1379,16 +1379,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "9.6.20",
|
"version": "9.6.21",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "49d7820565836236411f5dc002d16dd689cde42f"
|
"reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa",
|
||||||
"reference": "49d7820565836236411f5dc002d16dd689cde42f",
|
"reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1403,7 +1403,7 @@
|
||||||
"phar-io/manifest": "^2.0.4",
|
"phar-io/manifest": "^2.0.4",
|
||||||
"phar-io/version": "^3.2.1",
|
"phar-io/version": "^3.2.1",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
"phpunit/php-code-coverage": "^9.2.31",
|
"phpunit/php-code-coverage": "^9.2.32",
|
||||||
"phpunit/php-file-iterator": "^3.0.6",
|
"phpunit/php-file-iterator": "^3.0.6",
|
||||||
"phpunit/php-invoker": "^3.1.1",
|
"phpunit/php-invoker": "^3.1.1",
|
||||||
"phpunit/php-text-template": "^2.0.4",
|
"phpunit/php-text-template": "^2.0.4",
|
||||||
|
@ -1462,7 +1462,7 @@
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20"
|
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1478,7 +1478,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-07-10T11:45:39+00:00"
|
"time": "2024-09-19T10:50:18+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/http-client",
|
"name": "psr/http-client",
|
||||||
|
@ -2656,20 +2656,20 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.30.0",
|
"version": "v1.31.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
|
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1"
|
"php": ">=7.2"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"ext-mbstring": "*"
|
"ext-mbstring": "*"
|
||||||
|
@ -2716,7 +2716,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -2732,7 +2732,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-06-19T12:30:46+00:00"
|
"time": "2024-09-09T11:45:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "theseer/tokenizer",
|
"name": "theseer/tokenizer",
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
#!/usr/bin/php
|
|
||||||
<?php
|
|
||||||
$internalUse = $argv[1] ?? null;
|
|
||||||
if ($internalUse !== "--internal-use") exit("Wrong args");
|
|
||||||
$paramsfile = $argv[2] ?? null;
|
|
||||||
if (!file_exists($paramsfile)) exit("Bad params file");
|
|
||||||
$argc -= 2;
|
|
||||||
$argv = array_merge(
|
|
||||||
array_slice($argv, 0, 1),
|
|
||||||
array_slice($argv, 3),
|
|
||||||
);
|
|
||||||
|
|
||||||
$app_params = unserialize(file_get_contents($paramsfile));
|
|
||||||
@unlink($paramsfile);
|
|
||||||
require $app_params["vendor"]["autoload"];
|
|
||||||
|
|
||||||
use nur\cli\Application;
|
|
||||||
use nur\sery\output\msg;
|
|
||||||
use nur\sery\wip\app\app;
|
|
||||||
use nur\sery\app\launcher;
|
|
||||||
use nur\yaml;
|
|
||||||
|
|
||||||
class _LaunchApp extends Application {
|
|
||||||
const NAME = "_launch";
|
|
||||||
const USE_LOGFILE = true;
|
|
||||||
|
|
||||||
const ACTION_INFOS = 0, ACTION_START = 1, ACTION_STOP = 2;
|
|
||||||
|
|
||||||
const ARGS = [
|
|
||||||
"merge" => parent::ARGS,
|
|
||||||
"purpose" => "lancer une tâche de fond",
|
|
||||||
"usage" => "ApplicationClass args...",
|
|
||||||
|
|
||||||
["-s", "--start", "name" => "action", "value" => self::ACTION_START,
|
|
||||||
"help" => "démarrer la tâche, c'est la valeur par défaut"
|
|
||||||
],
|
|
||||||
["-k", "--stop", "name" => "action", "value" => self::ACTION_STOP,
|
|
||||||
"help" => "arrêter la tâche"
|
|
||||||
],
|
|
||||||
["-i", "--infos", "name" => "action", "value" => self::ACTION_INFOS,
|
|
||||||
"help" => "afficher des informations sur la tâche"
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $action = self::ACTION_START;
|
|
||||||
|
|
||||||
protected $args;
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
$appClass = $this->args[0] ?? null;
|
|
||||||
if ($appClass === null) {
|
|
||||||
msg::error("Vous devez spécifier la classe de l'application");
|
|
||||||
self::die();
|
|
||||||
} elseif (!class_exists($appClass)) {
|
|
||||||
msg::error("$appClass: Cette classe n'existe pas");
|
|
||||||
self::die();
|
|
||||||
}
|
|
||||||
$args = array_slice($this->args, 1);
|
|
||||||
|
|
||||||
$useRunfile = constant("$appClass::USE_RUNFILE");
|
|
||||||
if (!$useRunfile) {
|
|
||||||
msg::error("Cette application ne supporte pas l'usage de runfile");
|
|
||||||
self::die();
|
|
||||||
}
|
|
||||||
|
|
||||||
$runfile = app::with($appClass, self::$internal_use_app_params)->getRunfile();
|
|
||||||
switch ($this->action) {
|
|
||||||
case self::ACTION_START:
|
|
||||||
launcher::_start($args, $runfile);
|
|
||||||
break;
|
|
||||||
case self::ACTION_STOP:
|
|
||||||
launcher::_stop($runfile);
|
|
||||||
break;
|
|
||||||
case self::ACTION_INFOS:
|
|
||||||
yaml::dump($runfile->read());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_LaunchApp::internal_use_set_app_params($app_params);
|
|
||||||
_LaunchApp::run();
|
|
|
@ -12,7 +12,7 @@ use nur\path;
|
||||||
use nur\sery\app\launcher;
|
use nur\sery\app\launcher;
|
||||||
use nur\sery\app\RunFile;
|
use nur\sery\app\RunFile;
|
||||||
use nur\sery\cl;
|
use nur\sery\cl;
|
||||||
use nur\sery\wip\app\app;
|
use nur\sery\app\app;
|
||||||
use nur\sery\output\log as nlog;
|
use nur\sery\output\log as nlog;
|
||||||
use nur\sery\output\msg as nmsg;
|
use nur\sery\output\msg as nmsg;
|
||||||
use nur\sery\output\console as nconsole;
|
use nur\sery\output\console as nconsole;
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
namespace nur\sery\app;
|
namespace nur\sery\app;
|
||||||
|
|
||||||
|
use nur\sery\A;
|
||||||
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\os\sh;
|
||||||
use nur\sery\output\msg;
|
use nur\sery\output\msg;
|
||||||
use nur\sery\php\time\DateTime;
|
use nur\sery\php\time\DateTime;
|
||||||
|
use nur\sery\php\time\Elapsed;
|
||||||
use nur\sery\str;
|
use nur\sery\str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,21 +21,21 @@ class RunFile {
|
||||||
|
|
||||||
const NAME = null;
|
const NAME = null;
|
||||||
|
|
||||||
function __construct(?string $name, string $file, ?string $logfile=null) {
|
function __construct(?string $name, string $file, ?string $outfile=null) {
|
||||||
$file = path::ensure_ext($file, self::RUN_EXT);
|
$file = path::ensure_ext($file, self::RUN_EXT);
|
||||||
$this->name = $name ?? static::NAME;
|
$this->name = $name ?? static::NAME;
|
||||||
$this->file = new SharedFile($file);
|
$this->file = new SharedFile($file);
|
||||||
$this->logfile = $logfile;
|
$this->outfile = $outfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ?string $name;
|
protected ?string $name;
|
||||||
|
|
||||||
protected SharedFile $file;
|
protected SharedFile $file;
|
||||||
|
|
||||||
protected ?string $logfile;
|
protected ?string $outfile;
|
||||||
|
|
||||||
function getLogfile(): ?string {
|
function getOutfile(): ?string {
|
||||||
return $this->logfile;
|
return $this->outfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function merge(array $data, array $merge): array {
|
protected static function merge(array $data, array $merge): array {
|
||||||
|
@ -41,29 +44,23 @@ class RunFile {
|
||||||
], $merge);
|
], $merge);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function initData(bool $forStart=true): array {
|
protected function initData(): array {
|
||||||
if ($forStart) {
|
|
||||||
$pid = posix_getpid();
|
|
||||||
$dateStart = new DateTime();
|
|
||||||
} else {
|
|
||||||
$pid = $dateStart = null;
|
|
||||||
}
|
|
||||||
return [
|
return [
|
||||||
"name" => $this->name,
|
"name" => $this->name,
|
||||||
"id" => bin2hex(random_bytes(16)),
|
"pgid" => null,
|
||||||
"pg_pid" => null,
|
"pid" => null,
|
||||||
"pid" => $pid,
|
|
||||||
"serial" => 0,
|
"serial" => 0,
|
||||||
# lock
|
# lock
|
||||||
"locked" => false,
|
"locked" => false,
|
||||||
"date_lock" => null,
|
"date_lock" => null,
|
||||||
"date_release" => null,
|
"date_release" => null,
|
||||||
# run
|
# run
|
||||||
"logfile" => $this->logfile,
|
"logfile" => $this->outfile,
|
||||||
"date_start" => $dateStart,
|
"date_start" => null,
|
||||||
"date_stop" => null,
|
"date_stop" => null,
|
||||||
"exitcode" => null,
|
"exitcode" => null,
|
||||||
"is_done" => null,
|
"is_reaped" => null,
|
||||||
|
"is_ack_done" => null,
|
||||||
# action
|
# action
|
||||||
"action" => null,
|
"action" => null,
|
||||||
"action_date_start" => null,
|
"action_date_start" => null,
|
||||||
|
@ -73,9 +70,19 @@ class RunFile {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reset(bool $delete=false) {
|
||||||
|
$file = $this->file;
|
||||||
|
if ($delete) {
|
||||||
|
$file->close();
|
||||||
|
unlink($file->getFile());
|
||||||
|
} else {
|
||||||
|
$file->ftruncate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function read(): array {
|
function read(): array {
|
||||||
$data = $this->file->unserialize();
|
$data = $this->file->unserialize();
|
||||||
if (!is_array($data)) $data = $this->initData(false);
|
if (!is_array($data)) $data = $this->initData();
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +91,7 @@ class RunFile {
|
||||||
$file->lockWrite();
|
$file->lockWrite();
|
||||||
$data = $file->unserialize(null, false, true);
|
$data = $file->unserialize(null, false, true);
|
||||||
if (!is_array($data)) {
|
if (!is_array($data)) {
|
||||||
$data = $this->initData(false);
|
$data = $this->initData();
|
||||||
$file->ftruncate();
|
$file->ftruncate();
|
||||||
$file->serialize($data, false, true);
|
$file->serialize($data, false, true);
|
||||||
}
|
}
|
||||||
|
@ -160,14 +167,38 @@ class RunFile {
|
||||||
# cycle de vie de l'application
|
# cycle de vie de l'application
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* indiquer que l'application démarre. l'état est entièrement réinitialisé,
|
* Préparer le démarrage de l'application. Cette méhode est appelée par un
|
||||||
* sauf le PID du leader qui est laissé en l'état
|
* script externe qui doit préparer le démarrage du script
|
||||||
|
*
|
||||||
|
* - démarrer un groupe de process dont le process courant est le leader
|
||||||
*/
|
*/
|
||||||
|
function wfPrepare(?int &$pgid=null): void {
|
||||||
|
$this->update(function (array $data) use (&$pgid) {
|
||||||
|
posix_setsid();
|
||||||
|
$pgid = posix_getpid();
|
||||||
|
return cl::merge($this->initData(), [
|
||||||
|
"pgid" => $pgid,
|
||||||
|
"pid" => null,
|
||||||
|
"date_start" => new DateTime(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** indiquer que l'application démarre. */
|
||||||
function wfStart(): void {
|
function wfStart(): void {
|
||||||
$this->update(function (array $data) {
|
$this->update(function (array $data) {
|
||||||
return cl::merge($this->initData(), [
|
$pid = posix_getpid();
|
||||||
"pg_pid" => $data["pg_pid"],
|
if ($data["pgid"] !== null) {
|
||||||
|
A::merge($data, [
|
||||||
|
"pid" => $pid,
|
||||||
]);
|
]);
|
||||||
|
} else {
|
||||||
|
$data = cl::merge($this->initData(), [
|
||||||
|
"pid" => $pid,
|
||||||
|
"date_start" => new DateTime(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,13 +214,12 @@ class RunFile {
|
||||||
return $data["date_start"] !== null && $data["date_stop"] === null;
|
return $data["date_start"] !== null && $data["date_stop"] === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function _getCid(array $data=null): int {
|
||||||
* vérifier si l'application marquée comme démarrée tourne réellement
|
if ($data["pgid"] !== null) return -$data["pgid"];
|
||||||
*/
|
else return $data["pid"];
|
||||||
function isRunning(?array $data=null): bool {
|
}
|
||||||
$data ??= $this->read();
|
|
||||||
if ($data["date_start"] === null) return false;
|
function _isRunning(array $data=null): bool {
|
||||||
if ($data["date_stop"] !== null) return false;
|
|
||||||
if (!posix_kill($data["pid"], 0)) {
|
if (!posix_kill($data["pid"], 0)) {
|
||||||
switch (posix_get_last_error()) {
|
switch (posix_get_last_error()) {
|
||||||
case 1: #PCNTL_EPERM:
|
case 1: #PCNTL_EPERM:
|
||||||
|
@ -208,10 +238,22 @@ class RunFile {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vérifier si l'application marquée comme démarrée tourne réellement
|
||||||
|
*/
|
||||||
|
function isRunning(?array $data=null): bool {
|
||||||
|
$data ??= $this->read();
|
||||||
|
if ($data["date_start"] === null) return false;
|
||||||
|
if ($data["date_stop"] !== null) return false;
|
||||||
|
return $this->_isRunning($data);
|
||||||
|
}
|
||||||
|
|
||||||
/** indiquer que l'application s'arrête */
|
/** indiquer que l'application s'arrête */
|
||||||
function wfStop(): void {
|
function wfStop(): void {
|
||||||
$this->update(function (array $data) {
|
$this->update(function (array $data) {
|
||||||
return ["date_stop" => new DateTime()];
|
return [
|
||||||
|
"date_stop" => new DateTime(),
|
||||||
|
];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,88 +270,75 @@ class RunFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** après l'arrêt de l'application, mettre à jour le code de retour */
|
/** après l'arrêt de l'application, mettre à jour le code de retour */
|
||||||
function wfStopped(int $exitcode): void {
|
function wfReaped(int $exitcode): void {
|
||||||
$this->update(function (array $data) use ($exitcode) {
|
$this->update(function (array $data) use ($exitcode) {
|
||||||
return [
|
return [
|
||||||
"pg_pid" => null,
|
"pgid" => null,
|
||||||
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
||||||
"exitcode" => $exitcode,
|
"exitcode" => $exitcode,
|
||||||
|
"is_reaped" => true,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static function kill(int $pid, int $signal, ?string &$reason=null): bool {
|
||||||
* comme {@link self::isStopped()} mais ne renvoie true qu'une seule fois si
|
if (!posix_kill($pid, $signal)) {
|
||||||
* $updateDone==true
|
switch (posix_get_last_error()) {
|
||||||
*/
|
case PCNTL_ESRCH:
|
||||||
function isDone(?array &$data=null, bool $updateDone=true): bool {
|
$reason = "process inexistant";
|
||||||
$done = false;
|
break;
|
||||||
$this->update(function (array $ldata) use (&$done, &$data, $updateDone) {
|
case PCNTL_EPERM:
|
||||||
$data = $ldata;
|
$reason = "process non accessible";
|
||||||
if ($data["date_start"] === null || $data["date_stop"] === null || $data["is_done"]) {
|
break;
|
||||||
|
case PCNTL_EINVAL:
|
||||||
|
$reason = "signal invalide";
|
||||||
|
break;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$done = true;
|
return true;
|
||||||
if ($updateDone) return ["is_done" => $done];
|
|
||||||
else return null;
|
|
||||||
});
|
|
||||||
return $done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
function wfKill(?string &$reason=null): bool {
|
||||||
# gestion des actions
|
$data = $this->read();
|
||||||
|
$pid = $this->_getCid($data);
|
||||||
/** indiquer le début d'une action */
|
$stopped = false;
|
||||||
function action(?string $title, ?int $maxSteps=null): void {
|
$timeout = 10;
|
||||||
$this->update(function (array $data) use ($title, $maxSteps) {
|
$delay = 300000;
|
||||||
return [
|
while (--$timeout >= 0) {
|
||||||
"action" => $title,
|
if (!self::kill($pid, SIGTERM, $reason)) return false;
|
||||||
"action_date_start" => new DateTime(),
|
usleep($delay);
|
||||||
"action_max_step" => $maxSteps,
|
$delay = 1000000; // attendre 1 seconde à partir de la deuxième fois
|
||||||
"action_current_step" => 0,
|
if (!$this->_isRunning($data)) {
|
||||||
];
|
$stopped = true;
|
||||||
});
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** indiquer qu'une étape est franchie dans l'action en cours */
|
|
||||||
function step(int $nbSteps=1): void {
|
|
||||||
$this->update(function (array $data) use ($nbSteps) {
|
|
||||||
return [
|
|
||||||
"action_date_step" => new DateTime(),
|
|
||||||
"action_current_step" => $data["action_current_step"] + $nbSteps,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
if (!$stopped) {
|
||||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
$timeout = 3;
|
||||||
# Divers
|
$delay = 300000;
|
||||||
|
while (--$timeout >= 0) {
|
||||||
function getLockFile(?string $name=null, ?string $title=null): LockFile {
|
if (!self::kill($pid, SIGKILL, $reason)) return false;
|
||||||
$ext = self::LOCK_EXT;
|
usleep($delay);
|
||||||
if ($name !== null) $ext = ".$name$ext";
|
$delay = 1000000; // attendre 1 seconde à partir de la deuxième fois
|
||||||
$file = path::ensure_ext($this->file->getFile(), $ext, self::RUN_EXT);
|
if (!$this->_isRunning($data)) {
|
||||||
$name = str::join("/", [$this->name, $name]);
|
$stopped = true;
|
||||||
return new LockFile($file, $name, $title);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
}
|
||||||
# Gestionnaire de tâches (tm_*)
|
if ($stopped) {
|
||||||
|
sh::_waitpid($pid, $exitcode);
|
||||||
/** démarrer un groupe de process dont le process courant est le leader */
|
$this->wfReaped($exitcode);
|
||||||
function tm_startPg(): void {
|
}
|
||||||
$this->update(function (array $data) {
|
return $stopped;
|
||||||
posix_setsid();
|
|
||||||
return [
|
|
||||||
"pg_pid" => posix_getpid(),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vérifier si on est dans le cas où la tâche devrait tourner mais en réalité
|
* vérifier si on est dans le cas où la tâche devrait tourner mais en réalité
|
||||||
* ce n'est pas le cas
|
* ce n'est pas le cas
|
||||||
*/
|
*/
|
||||||
function tm_isUndead(?int $pid=null): bool {
|
function _isUndead(?int $pid=null): bool {
|
||||||
$data = $this->read();
|
$data = $this->read();
|
||||||
if ($data["date_start"] === null) return false;
|
if ($data["date_start"] === null) return false;
|
||||||
if ($data["date_stop"] !== null) return false;
|
if ($data["date_stop"] !== null) return false;
|
||||||
|
@ -332,23 +361,104 @@ class RunFile {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tm_isReapable(): bool {
|
/**
|
||||||
$data = $this->read();
|
* comme {@link self::isStopped()} mais ne renvoie true qu'une seule fois si
|
||||||
return $data["date_stop"] !== null && $data["exitcode"] === null;
|
* $updateDone==true
|
||||||
|
*/
|
||||||
|
function isDone(?array &$data=null, bool $updateDone=true): bool {
|
||||||
|
$done = false;
|
||||||
|
$this->update(function (array $ldata) use (&$done, &$data, $updateDone) {
|
||||||
|
$data = $ldata;
|
||||||
|
if ($data["date_start"] === null || $data["date_stop"] === null || $data["is_ack_done"]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$done = true;
|
||||||
|
if ($updateDone) return ["is_ack_done" => $done];
|
||||||
|
else return null;
|
||||||
|
});
|
||||||
|
return $done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** marquer la tâche comme terminée */
|
function getDesc(?array $data=null): ?string {
|
||||||
function tm_reap(?int $pid=null): void {
|
$data ??= $this->read();
|
||||||
$data = $this->read();
|
$desc = $data["name"];
|
||||||
$pid ??= $data["pid"];
|
$dateStart = $data["date_start"];
|
||||||
pcntl_waitpid($pid, $status);
|
$dateStop = $data["date_stop"];
|
||||||
$exitcode = pcntl_wifexited($status)? pcntl_wexitstatus($status): 127;
|
$exitcode = $data["exitcode"];
|
||||||
$this->update(function (array $data) use ($exitcode) {
|
if ($exitcode !== null) $exitcode = "\nCode de retour $exitcode";
|
||||||
|
if (!$this->wasStarted($data)) {
|
||||||
|
return "$desc: pas encore démarré";
|
||||||
|
} elseif ($this->isRunning($data)) {
|
||||||
|
$sinceStart = Elapsed::format_since($dateStart);
|
||||||
|
$started = "\nDémarré depuis $dateStart ($sinceStart)";
|
||||||
|
return "$desc: EN COURS pid $data[pid]$started";
|
||||||
|
} elseif ($this->isStopped($data)) {
|
||||||
|
$duration = "\nDurée ".Elapsed::format_delay($dateStart, $dateStop);
|
||||||
|
$sinceStop = Elapsed::format_since($dateStop);
|
||||||
|
$stopped = "\nArrêtée $sinceStop le $dateStop";
|
||||||
|
$reaped = $data["is_reaped"]? ", reaped": null;
|
||||||
|
$done = $data["is_ack_done"]? ", ACK done": null;
|
||||||
|
return "$desc: TERMINEE$duration$stopped$exitcode$reaped$done";
|
||||||
|
} else {
|
||||||
|
$stopped = $dateStop? "\nArrêtée le $dateStop": null;
|
||||||
|
return "$desc: CRASHED\nCommencé le $dateStart$stopped$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) {
|
||||||
return [
|
return [
|
||||||
"pg_pid" => null,
|
"action" => $title,
|
||||||
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
"action_date_start" => new DateTime(),
|
||||||
"exitcode" => $data["exitcode"] ?? $exitcode,
|
"action_max_step" => $maxSteps,
|
||||||
|
"action_current_step" => 0,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
app::_dispatch_signals();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** indiquer qu'une étape est franchie dans l'action en cours */
|
||||||
|
function step(int $nbSteps=1): void {
|
||||||
|
$this->update(function (array $data) use ($nbSteps) {
|
||||||
|
return [
|
||||||
|
"action_date_step" => new DateTime(),
|
||||||
|
"action_current_step" => $data["action_current_step"] + $nbSteps,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
app::_dispatch_signals();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActionDesc(?array $data=null): ?string {
|
||||||
|
$data ??= $this->read();
|
||||||
|
$action = $data["action"];
|
||||||
|
if ($action !== null) {
|
||||||
|
$date ??= $data["action_date_step"];
|
||||||
|
$date ??= $data["action_date_start"];
|
||||||
|
if ($date !== null) $action = "$date $action";
|
||||||
|
$action = "Etape en cours: $action";
|
||||||
|
$current = $data["action_current_step"];
|
||||||
|
$max = $data["action_max_step"];
|
||||||
|
if ($current !== null && $max !== null) {
|
||||||
|
$action .= " ($current / $max)";
|
||||||
|
} elseif ($current !== null) {
|
||||||
|
$action .= " ($current)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $action;
|
||||||
|
}
|
||||||
|
|
||||||
|
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
# Divers
|
||||||
|
|
||||||
|
function getLockFile(?string $name=null, ?string $title=null): LockFile {
|
||||||
|
$ext = self::LOCK_EXT;
|
||||||
|
if ($name !== null) $ext = ".$name$ext";
|
||||||
|
$file = path::ensure_ext($this->file->getFile(), $ext, self::RUN_EXT);
|
||||||
|
$name = str::join("/", [$this->name, $name]);
|
||||||
|
return new LockFile($file, $name, $title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\app;
|
namespace nur\sery\app;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use nur\sery\A;
|
use nur\sery\A;
|
||||||
|
use nur\sery\app\cli\Application;
|
||||||
use nur\sery\cl;
|
use nur\sery\cl;
|
||||||
use nur\sery\ExitError;
|
use nur\sery\ExitError;
|
||||||
use nur\sery\os\path;
|
use nur\sery\os\path;
|
||||||
|
@ -10,10 +10,8 @@ use nur\sery\os\sh;
|
||||||
use nur\sery\php\func;
|
use nur\sery\php\func;
|
||||||
use nur\sery\str;
|
use nur\sery\str;
|
||||||
use nur\sery\ValueException;
|
use nur\sery\ValueException;
|
||||||
use nur\sery\wip\app\cli\Application;
|
|
||||||
|
|
||||||
#XXX une réécriture de app, qui remplacera app à terme
|
class app {
|
||||||
class app2 {
|
|
||||||
private static function isa_Application($app): bool {
|
private static function isa_Application($app): bool {
|
||||||
if (!is_string($app)) return false;
|
if (!is_string($app)) return false;
|
||||||
return $app === Application::class || is_subclass_of($app, Application::class);
|
return $app === Application::class || is_subclass_of($app, Application::class);
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\app;
|
namespace nur\sery\app;
|
||||||
|
|
||||||
use nur\sery\A;
|
use nur\sery\A;
|
||||||
use nur\sery\str;
|
use nur\sery\str;
|
|
@ -4,11 +4,11 @@
|
||||||
# - NULIB_APP_app_params : paramètres du projet
|
# - NULIB_APP_app_params : paramètres du projet
|
||||||
|
|
||||||
use nur\sery\os\path;
|
use nur\sery\os\path;
|
||||||
use nur\sery\wip\app\app2;
|
use nur\sery\app\app;
|
||||||
|
|
||||||
if ($argc <= 1) die("invalid arguments");
|
if ($argc <= 1) die("invalid arguments");
|
||||||
|
|
||||||
app2::init(NULIB_APP_app_params);
|
app::init(NULIB_APP_app_params);
|
||||||
|
|
||||||
$app = $argv[1];
|
$app = $argv[1];
|
||||||
if (class_exists($app)) {
|
if (class_exists($app)) {
|
||||||
|
@ -17,13 +17,13 @@ if (class_exists($app)) {
|
||||||
$app::run();
|
$app::run();
|
||||||
} elseif (is_executable($app)) {
|
} elseif (is_executable($app)) {
|
||||||
# la configuration est passée par une variable d'environnement
|
# la configuration est passée par une variable d'environnement
|
||||||
app2::params_putenv();
|
app::params_putenv();
|
||||||
pcntl_exec($app, array_slice($argv, 1));
|
pcntl_exec($app, array_slice($argv, 1));
|
||||||
} else {
|
} else {
|
||||||
# la configuration est celle actuellement chargée
|
# la configuration est celle actuellement chargée
|
||||||
array_splice($argv, 0, 1); $argc--;
|
array_splice($argv, 0, 1); $argc--;
|
||||||
$name = preg_replace('/\.php$/', "", path::basename($app));
|
$name = preg_replace('/\.php$/', "", path::basename($app));
|
||||||
app2::init([
|
app::init([
|
||||||
"name" => $name,
|
"name" => $name,
|
||||||
]);
|
]);
|
||||||
require $app;
|
require $app;
|
||||||
|
|
|
@ -7,12 +7,12 @@ require __DIR__.'/../vendor/autoload.php';
|
||||||
# (par défaut c'est le répertoire bin/) et modifier les paramètres si nécessaire
|
# (par défaut c'est le répertoire bin/) et modifier les paramètres si nécessaire
|
||||||
|
|
||||||
use nur\sery\tools\BgLauncherApp;
|
use nur\sery\tools\BgLauncherApp;
|
||||||
use nur\sery\wip\app\app2;
|
use nur\sery\app\app;
|
||||||
|
|
||||||
# chemin vers le lanceur PHP
|
# chemin vers le lanceur PHP
|
||||||
const NULIB_APP_app_launcher = __DIR__.'/../_cli/_launcher.php';
|
const NULIB_APP_app_launcher = __DIR__.'/../_cli/_launcher.php';
|
||||||
|
|
||||||
app2::init([
|
app::init([
|
||||||
"projdir" => __DIR__ . '/..',
|
"projdir" => __DIR__ . '/..',
|
||||||
"appcode" => \app\config\bootstrap::APPCODE,
|
"appcode" => \app\config\bootstrap::APPCODE,
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -1,137 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace nur\sery\app;
|
|
||||||
|
|
||||||
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\app;
|
|
||||||
|
|
||||||
#XXX sera obsolète quand cli\bg_launcher sera mis en prod
|
|
||||||
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 = app::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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ namespace nur\sery\os;
|
||||||
use nur\sery\cl;
|
use nur\sery\cl;
|
||||||
use nur\sery\ExitError;
|
use nur\sery\ExitError;
|
||||||
use nur\sery\StateException;
|
use nur\sery\StateException;
|
||||||
use nur\sery\wip\app\app2;
|
use nur\sery\app\app;
|
||||||
|
|
||||||
class sh {
|
class sh {
|
||||||
static final function _quote(string $value): string {
|
static final function _quote(string $value): string {
|
||||||
|
@ -140,7 +140,7 @@ class sh {
|
||||||
pcntl_waitpid($pid, $status);
|
pcntl_waitpid($pid, $status);
|
||||||
if (pcntl_wifexited($status)) $retcode = pcntl_wexitstatus($status);
|
if (pcntl_wifexited($status)) $retcode = pcntl_wexitstatus($status);
|
||||||
elseif (pcntl_wifsignaled($status)) $retcode = -pcntl_wtermsig($status);
|
elseif (pcntl_wifsignaled($status)) $retcode = -pcntl_wtermsig($status);
|
||||||
else $retcode = app2::EC_FORK_CHILD;
|
else $retcode = app::EC_FORK_CHILD;
|
||||||
return $retcode == 0;
|
return $retcode == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ class sh {
|
||||||
$pid = pcntl_fork();
|
$pid = pcntl_fork();
|
||||||
if ($pid == -1) {
|
if ($pid == -1) {
|
||||||
// parent, impossible de forker
|
// parent, impossible de forker
|
||||||
throw new ExitError(app2::EC_FORK_PARENT, "unable to fork");
|
throw new ExitError(app::EC_FORK_PARENT, "unable to fork");
|
||||||
} elseif ($pid) {
|
} elseif ($pid) {
|
||||||
// parent, fork ok
|
// parent, fork ok
|
||||||
if ($wait) return self::_waitpid($pid, $retcode);
|
if ($wait) return self::_waitpid($pid, $retcode);
|
||||||
|
|
|
@ -6,9 +6,9 @@ use nur\sery\os\path;
|
||||||
use nur\sery\os\proc\Cmd;
|
use nur\sery\os\proc\Cmd;
|
||||||
use nur\sery\os\sh;
|
use nur\sery\os\sh;
|
||||||
use nur\sery\output\msg;
|
use nur\sery\output\msg;
|
||||||
use nur\sery\wip\app\app2;
|
use nur\sery\app\app;
|
||||||
use nur\sery\wip\app\cli\Application;
|
use nur\sery\app\cli\Application;
|
||||||
use nur\sery\wip\app\RunFile;
|
use nur\sery\app\RunFile;
|
||||||
use nur\yaml;
|
use nur\yaml;
|
||||||
|
|
||||||
class BgLauncherApp extends Application {
|
class BgLauncherApp extends Application {
|
||||||
|
@ -58,16 +58,16 @@ class BgLauncherApp extends Application {
|
||||||
self::die("Cette application ne supporte le lancement en tâche de fond");
|
self::die("Cette application ne supporte le lancement en tâche de fond");
|
||||||
}
|
}
|
||||||
|
|
||||||
$runfile = app2::with($appClass)->getRunfile();
|
$runfile = app::with($appClass)->getRunfile();
|
||||||
switch ($this->action) {
|
switch ($this->action) {
|
||||||
case self::ACTION_START:
|
case self::ACTION_START:
|
||||||
$appClass::_manage_runfile(count($args), $args, $runfile);
|
$appClass::_manage_runfile(count($args), $args, $runfile);
|
||||||
if ($runfile->warnIfLocked()) self::exit(app2::EC_LOCKED);
|
if ($runfile->warnIfLocked()) self::exit(app::EC_LOCKED);
|
||||||
array_splice($args, 0, 0, [
|
array_splice($args, 0, 0, [
|
||||||
PHP_BINARY,
|
PHP_BINARY,
|
||||||
path::abspath(NULIB_APP_app_launcher),
|
path::abspath(NULIB_APP_app_launcher),
|
||||||
]);
|
]);
|
||||||
app2::params_putenv();
|
app::params_putenv();
|
||||||
self::_start($args, $runfile);
|
self::_start($args, $runfile);
|
||||||
break;
|
break;
|
||||||
case self::ACTION_STOP:
|
case self::ACTION_STOP:
|
||||||
|
@ -84,20 +84,18 @@ class BgLauncherApp extends Application {
|
||||||
$pid = pcntl_fork();
|
$pid = pcntl_fork();
|
||||||
if ($pid == -1) {
|
if ($pid == -1) {
|
||||||
# parent, impossible de forker
|
# parent, impossible de forker
|
||||||
throw new ExitError(app2::EC_FORK_PARENT, "Unable to fork");
|
throw new ExitError(app::EC_FORK_PARENT, "Unable to fork");
|
||||||
} elseif (!$pid) {
|
} elseif (!$pid) {
|
||||||
# child, fork ok
|
# child, fork ok
|
||||||
$runfile->wfPrepare($pid);
|
$runfile->wfPrepare($pid);
|
||||||
$outfile = $runfile->getOutfile() ?? "/tmp/NULIB_APP_app_console.out";
|
$outfile = $runfile->getOutfile() ?? "/tmp/NULIB_APP_app_console.out";
|
||||||
$exitcode = app2::EC_FORK_CHILD;
|
$exitcode = app::EC_FORK_CHILD;
|
||||||
try {
|
try {
|
||||||
# rediriger STDIN, STDOUT et STDERR
|
# rediriger STDIN, STDOUT et STDERR
|
||||||
fclose(STDIN);
|
fclose(fopen($outfile, "wb")); // vider le fichier
|
||||||
$in = fopen("/dev/null", "rb");
|
fclose(STDIN); $in = fopen("/dev/null", "rb");
|
||||||
fclose(STDOUT);
|
fclose(STDOUT); $out = fopen($outfile, "ab");
|
||||||
$out = fopen($outfile, "a+b");
|
fclose(STDERR); $err = fopen($outfile, "ab");
|
||||||
fclose(STDERR);
|
|
||||||
$err = fopen($outfile, "a+b");
|
|
||||||
# puis lancer la commande
|
# puis lancer la commande
|
||||||
$cmd = new Cmd($args);
|
$cmd = new Cmd($args);
|
||||||
$cmd->addSource("/g/init.env");
|
$cmd->addSource("/g/init.env");
|
||||||
|
|
|
@ -4,8 +4,8 @@ namespace nur\sery\tools;
|
||||||
use nur\sery\output\msg;
|
use nur\sery\output\msg;
|
||||||
use nur\sery\php\time\DateTime;
|
use nur\sery\php\time\DateTime;
|
||||||
use nur\sery\text\words;
|
use nur\sery\text\words;
|
||||||
use nur\sery\wip\app\app2;
|
use nur\sery\app\app;
|
||||||
use nur\sery\wip\app\cli\Application;
|
use nur\sery\app\cli\Application;
|
||||||
|
|
||||||
class SteamTrainApp extends Application {
|
class SteamTrainApp extends Application {
|
||||||
const PROJDIR = __DIR__.'/../..';
|
const PROJDIR = __DIR__.'/../..';
|
||||||
|
@ -33,13 +33,13 @@ EOT,
|
||||||
protected bool $installSignalHandler = true;
|
protected bool $installSignalHandler = true;
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
if ($this->installSignalHandler) app2::install_signal_handler();
|
if ($this->installSignalHandler) app::install_signal_handler();
|
||||||
$count = intval($this->count);
|
$count = intval($this->count);
|
||||||
msg::info("Starting train for ".words::q($count, "step#s"));
|
msg::info("Starting train for ".words::q($count, "step#s"));
|
||||||
app2::action("Running train...", $count);
|
app::action("Running train...", $count);
|
||||||
for ($i = 1; $i <= $count; $i++) {
|
for ($i = 1; $i <= $count; $i++) {
|
||||||
msg::print("Tchou-tchou! x $i");
|
msg::print("Tchou-tchou! x $i");
|
||||||
app2::step();
|
app::step();
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
msg::info("Stopping train at ".new DateTime());
|
msg::info("Stopping train at ".new DateTime());
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
namespace nur\sery\wip\app {
|
namespace nur\sery\wip\app {
|
||||||
use nulib\tests\TestCase;
|
use nulib\tests\TestCase;
|
||||||
use nur\sery\wip\app\impl\config;
|
use nur\sery\wip\app\impl\config;
|
||||||
use nur\sery\wip\app\impl\myapp2;
|
use nur\sery\wip\app\impl\myapp;
|
||||||
use nur\sery\wip\app\impl\MyApplication1;
|
use nur\sery\wip\app\impl\MyApplication1;
|
||||||
use nur\sery\wip\app\impl\MyApplication2;
|
use nur\sery\wip\app\impl\MyApplication2;
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ namespace nur\sery\wip\app {
|
||||||
$projdir = config::get_projdir();
|
$projdir = config::get_projdir();
|
||||||
$cwd = getcwd();
|
$cwd = getcwd();
|
||||||
|
|
||||||
myapp2::reset();
|
myapp::reset();
|
||||||
$app1 = myapp2::with(MyApplication1::class);
|
$app1 = myapp::with(MyApplication1::class);
|
||||||
self::assertSame([
|
self::assertSame([
|
||||||
"projdir" => $projdir,
|
"projdir" => $projdir,
|
||||||
"vendor" => [
|
"vendor" => [
|
||||||
|
@ -30,7 +30,7 @@ namespace nur\sery\wip\app {
|
||||||
"title" => null,
|
"title" => null,
|
||||||
], $app1->getParams());
|
], $app1->getParams());
|
||||||
|
|
||||||
$app2 = myapp2::with(MyApplication2::class, $app1);
|
$app2 = myapp::with(MyApplication2::class, $app1);
|
||||||
self::assertSame([
|
self::assertSame([
|
||||||
"projdir" => $projdir,
|
"projdir" => $projdir,
|
||||||
"vendor" => [
|
"vendor" => [
|
||||||
|
@ -53,8 +53,8 @@ namespace nur\sery\wip\app {
|
||||||
$projdir = config::get_projdir();
|
$projdir = config::get_projdir();
|
||||||
$cwd = getcwd();
|
$cwd = getcwd();
|
||||||
|
|
||||||
myapp2::reset();
|
myapp::reset();
|
||||||
myapp2::init(MyApplication1::class);
|
myapp::init(MyApplication1::class);
|
||||||
self::assertSame([
|
self::assertSame([
|
||||||
"projdir" => $projdir,
|
"projdir" => $projdir,
|
||||||
"vendor" => [
|
"vendor" => [
|
||||||
|
@ -70,9 +70,9 @@ namespace nur\sery\wip\app {
|
||||||
"profile" => "devel",
|
"profile" => "devel",
|
||||||
"name" => "my-application1",
|
"name" => "my-application1",
|
||||||
"title" => null,
|
"title" => null,
|
||||||
], myapp2::get()->getParams());
|
], myapp::get()->getParams());
|
||||||
|
|
||||||
myapp2::init(MyApplication2::class);
|
myapp::init(MyApplication2::class);
|
||||||
self::assertSame([
|
self::assertSame([
|
||||||
"projdir" => $projdir,
|
"projdir" => $projdir,
|
||||||
"vendor" => [
|
"vendor" => [
|
||||||
|
@ -88,7 +88,7 @@ namespace nur\sery\wip\app {
|
||||||
"profile" => "devel",
|
"profile" => "devel",
|
||||||
"name" => "my-application2",
|
"name" => "my-application2",
|
||||||
"title" => null,
|
"title" => null,
|
||||||
], myapp2::get()->getParams());
|
], myapp::get()->getParams());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ namespace nur\sery\wip\app {
|
||||||
namespace nur\sery\wip\app\impl {
|
namespace nur\sery\wip\app\impl {
|
||||||
use nur\cli\Application2;
|
use nur\cli\Application2;
|
||||||
use nur\sery\os\path;
|
use nur\sery\os\path;
|
||||||
use nur\sery\wip\app\app2;
|
use nur\sery\app\app;
|
||||||
|
|
||||||
class config {
|
class config {
|
||||||
const PROJDIR = __DIR__.'/../../..';
|
const PROJDIR = __DIR__.'/../../..';
|
||||||
|
@ -106,7 +106,7 @@ namespace nur\sery\wip\app\impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class myapp2 extends app2 {
|
class myapp extends app {
|
||||||
static function reset(): void {
|
static function reset(): void {
|
||||||
self::$app = null;
|
self::$app = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace nur\sery\wip\app;
|
namespace nur\sery\wip\app;
|
||||||
|
|
||||||
use nulib\tests\TestCase;
|
use nulib\tests\TestCase;
|
||||||
|
use nur\sery\app\app;
|
||||||
|
|
||||||
class appTest extends TestCase {
|
class appTest extends TestCase {
|
||||||
function testVerifix_name() {
|
function testVerifix_name() {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace nur\sery\wip\app;
|
namespace nur\sery\wip\app;
|
||||||
|
|
||||||
use nulib\tests\TestCase;
|
use nulib\tests\TestCase;
|
||||||
|
use nur\sery\app\args;
|
||||||
|
|
||||||
class argsTest extends TestCase {
|
class argsTest extends TestCase {
|
||||||
function testFrom_array() {
|
function testFrom_array() {
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace nur\sery\wip\app;
|
|
||||||
|
|
||||||
use nur\sery\cl;
|
|
||||||
use nur\sery\file\SharedFile;
|
|
||||||
use nur\sery\output\msg;
|
|
||||||
use nur\sery\php\time\DateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LockFile: une classe qui permet à une application de verrouiller
|
|
||||||
* certaines actions
|
|
||||||
*/
|
|
||||||
class LockFile {
|
|
||||||
const NAME = null;
|
|
||||||
|
|
||||||
const TITLE = null;
|
|
||||||
|
|
||||||
function __construct($file, ?string $name=null, ?string $title=null) {
|
|
||||||
$this->file = new SharedFile($file);
|
|
||||||
$this->name = $name ?? static::NAME;
|
|
||||||
$this->title = $title ?? static::TITLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var SharedFile */
|
|
||||||
protected $file;
|
|
||||||
|
|
||||||
/** @var ?string */
|
|
||||||
protected $name;
|
|
||||||
|
|
||||||
/** @var ?string */
|
|
||||||
protected $title;
|
|
||||||
|
|
||||||
protected function initData(): array {
|
|
||||||
return [
|
|
||||||
"name" => $this->name,
|
|
||||||
"title" => $this->title,
|
|
||||||
"locked" => false,
|
|
||||||
"date_lock" => null,
|
|
||||||
"date_release" => null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function read(bool $close=true): array {
|
|
||||||
$data = $this->file->unserialize(null, $close);
|
|
||||||
if (!is_array($data)) $data = $this->initData();
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
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] -- $data[title]");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lock(?array &$data=null): bool {
|
|
||||||
$file = $this->file;
|
|
||||||
$data = $this->read(false);
|
|
||||||
if ($data["locked"]) {
|
|
||||||
$file->close();
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
$file->ftruncate();
|
|
||||||
$file->serialize(cl::merge($data, [
|
|
||||||
"locked" => true,
|
|
||||||
"date_lock" => new DateTime(),
|
|
||||||
"date_release" => null,
|
|
||||||
]));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function release(?array &$data=null): void {
|
|
||||||
$file = $this->file;
|
|
||||||
$data = $this->read(false);
|
|
||||||
$file->ftruncate();
|
|
||||||
$file->serialize(cl::merge($data, [
|
|
||||||
"locked" => false,
|
|
||||||
"date_release" => new DateTime(),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,464 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace nur\sery\wip\app;
|
|
||||||
|
|
||||||
use nur\sery\A;
|
|
||||||
use nur\sery\cl;
|
|
||||||
use nur\sery\file\SharedFile;
|
|
||||||
use nur\sery\os\path;
|
|
||||||
use nur\sery\os\sh;
|
|
||||||
use nur\sery\output\msg;
|
|
||||||
use nur\sery\php\time\DateTime;
|
|
||||||
use nur\sery\php\time\Elapsed;
|
|
||||||
use nur\sery\str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class RunFile: une classe permettant de suivre le fonctionnement d'une
|
|
||||||
* application qui tourne en tâche de fond
|
|
||||||
*/
|
|
||||||
class RunFile {
|
|
||||||
const RUN_EXT = ".run";
|
|
||||||
const LOCK_EXT = ".lock";
|
|
||||||
|
|
||||||
const NAME = null;
|
|
||||||
|
|
||||||
function __construct(?string $name, string $file, ?string $outfile=null) {
|
|
||||||
$file = path::ensure_ext($file, self::RUN_EXT);
|
|
||||||
$this->name = $name ?? static::NAME;
|
|
||||||
$this->file = new SharedFile($file);
|
|
||||||
$this->outfile = $outfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ?string $name;
|
|
||||||
|
|
||||||
protected SharedFile $file;
|
|
||||||
|
|
||||||
protected ?string $outfile;
|
|
||||||
|
|
||||||
function getOutfile(): ?string {
|
|
||||||
return $this->outfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function merge(array $data, array $merge): array {
|
|
||||||
return cl::merge($data, [
|
|
||||||
"serial" => $data["serial"] + 1,
|
|
||||||
], $merge);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function initData(): array {
|
|
||||||
return [
|
|
||||||
"name" => $this->name,
|
|
||||||
"pgid" => null,
|
|
||||||
"pid" => null,
|
|
||||||
"serial" => 0,
|
|
||||||
# lock
|
|
||||||
"locked" => false,
|
|
||||||
"date_lock" => null,
|
|
||||||
"date_release" => null,
|
|
||||||
# run
|
|
||||||
"logfile" => $this->outfile,
|
|
||||||
"date_start" => null,
|
|
||||||
"date_stop" => null,
|
|
||||||
"exitcode" => null,
|
|
||||||
"is_reaped" => null,
|
|
||||||
"is_ack_done" => null,
|
|
||||||
# action
|
|
||||||
"action" => null,
|
|
||||||
"action_date_start" => null,
|
|
||||||
"action_current_step" => null,
|
|
||||||
"action_max_step" => null,
|
|
||||||
"action_date_step" => null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset(bool $delete=false) {
|
|
||||||
$file = $this->file;
|
|
||||||
if ($delete) {
|
|
||||||
$file->close();
|
|
||||||
unlink($file->getFile());
|
|
||||||
} else {
|
|
||||||
$file->ftruncate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function read(): array {
|
|
||||||
$data = $this->file->unserialize();
|
|
||||||
if (!is_array($data)) $data = $this->initData();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
return [$file, $data];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function serialize(SharedFile $file, array $data, ?array $merge=null): void {
|
|
||||||
$file->ftruncate();
|
|
||||||
$file->serialize(self::merge($data, $merge), true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function update(callable $func): void {
|
|
||||||
/** @var SharedFile$file */
|
|
||||||
[$file, $data] = $this->willWrite();
|
|
||||||
$merge = call_user_func($func, $data);
|
|
||||||
if ($merge !== null && $merge !== false) {
|
|
||||||
$this->serialize($file, $data, $merge);
|
|
||||||
} else {
|
|
||||||
$file->cancelWrite();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function haveWorked(int $serial, ?int &$currentSerial=null, ?array $data=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 {
|
|
||||||
$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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Préparer le démarrage de l'application. Cette méhode est appelée par un
|
|
||||||
* script externe qui doit préparer le démarrage du script
|
|
||||||
*
|
|
||||||
* - démarrer un groupe de process dont le process courant est le leader
|
|
||||||
*/
|
|
||||||
function wfPrepare(?int &$pgid=null): void {
|
|
||||||
$this->update(function (array $data) use (&$pgid) {
|
|
||||||
posix_setsid();
|
|
||||||
$pgid = posix_getpid();
|
|
||||||
return cl::merge($this->initData(), [
|
|
||||||
"pgid" => $pgid,
|
|
||||||
"pid" => null,
|
|
||||||
"date_start" => new DateTime(),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** indiquer que l'application démarre. */
|
|
||||||
function wfStart(): void {
|
|
||||||
$this->update(function (array $data) {
|
|
||||||
$pid = posix_getpid();
|
|
||||||
if ($data["pgid"] !== null) {
|
|
||||||
A::merge($data, [
|
|
||||||
"pid" => $pid,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$data = cl::merge($this->initData(), [
|
|
||||||
"pid" => $pid,
|
|
||||||
"date_start" => new DateTime(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** tester si l'application a déjà été démarrée au moins une fois */
|
|
||||||
function wasStarted(?array $data=null): bool {
|
|
||||||
$data ??= $this->read();
|
|
||||||
return $data["date_start"] !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** tester si l'application est démarrée et non arrêtée */
|
|
||||||
function isStarted(?array $data=null): bool {
|
|
||||||
$data ??= $this->read();
|
|
||||||
return $data["date_start"] !== null && $data["date_stop"] === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getCid(array $data=null): int {
|
|
||||||
if ($data["pgid"] !== null) return -$data["pgid"];
|
|
||||||
else return $data["pid"];
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isRunning(array $data=null): bool {
|
|
||||||
if (!posix_kill($data["pid"], 0)) {
|
|
||||||
switch (posix_get_last_error()) {
|
|
||||||
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 3: #PCNTL_ESRCH:
|
|
||||||
# process inexistant
|
|
||||||
return false;
|
|
||||||
case 22: #PCNTL_EINVAL:
|
|
||||||
# ne devrait pas se produire
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# process existant auquel on a accès
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* vérifier si l'application marquée comme démarrée tourne réellement
|
|
||||||
*/
|
|
||||||
function isRunning(?array $data=null): bool {
|
|
||||||
$data ??= $this->read();
|
|
||||||
if ($data["date_start"] === null) return false;
|
|
||||||
if ($data["date_stop"] !== null) return false;
|
|
||||||
return $this->_isRunning($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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 au moins une fois */
|
|
||||||
function wasStopped(?array $data=null): bool {
|
|
||||||
$data ??= $this->read();
|
|
||||||
return $data["date_stop"] !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** tester si l'application a été démarrée puis arrêtée */
|
|
||||||
function isStopped(?array $data=null): bool {
|
|
||||||
$data ??= $this->read();
|
|
||||||
return $data["date_start"] !== null && $data["date_stop"] !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** après l'arrêt de l'application, mettre à jour le code de retour */
|
|
||||||
function wfReaped(int $exitcode): void {
|
|
||||||
$this->update(function (array $data) use ($exitcode) {
|
|
||||||
return [
|
|
||||||
"pgid" => null,
|
|
||||||
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
|
||||||
"exitcode" => $exitcode,
|
|
||||||
"is_reaped" => true,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function kill(int $pid, int $signal, ?string &$reason=null): bool {
|
|
||||||
if (!posix_kill($pid, $signal)) {
|
|
||||||
switch (posix_get_last_error()) {
|
|
||||||
case PCNTL_ESRCH:
|
|
||||||
$reason = "process inexistant";
|
|
||||||
break;
|
|
||||||
case PCNTL_EPERM:
|
|
||||||
$reason = "process non accessible";
|
|
||||||
break;
|
|
||||||
case PCNTL_EINVAL:
|
|
||||||
$reason = "signal invalide";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function wfKill(?string &$reason=null): bool {
|
|
||||||
$data = $this->read();
|
|
||||||
$pid = $this->_getCid($data);
|
|
||||||
$stopped = false;
|
|
||||||
$timeout = 10;
|
|
||||||
$delay = 300000;
|
|
||||||
while (--$timeout >= 0) {
|
|
||||||
if (!self::kill($pid, SIGTERM, $reason)) return false;
|
|
||||||
usleep($delay);
|
|
||||||
$delay = 1000000; // attendre 1 seconde à partir de la deuxième fois
|
|
||||||
if (!$this->_isRunning($data)) {
|
|
||||||
$stopped = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$stopped) {
|
|
||||||
$timeout = 3;
|
|
||||||
$delay = 300000;
|
|
||||||
while (--$timeout >= 0) {
|
|
||||||
if (!self::kill($pid, SIGKILL, $reason)) return false;
|
|
||||||
usleep($delay);
|
|
||||||
$delay = 1000000; // attendre 1 seconde à partir de la deuxième fois
|
|
||||||
if (!$this->_isRunning($data)) {
|
|
||||||
$stopped = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($stopped) {
|
|
||||||
sh::_waitpid($pid, $exitcode);
|
|
||||||
$this->wfReaped($exitcode);
|
|
||||||
}
|
|
||||||
return $stopped;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* vérifier si on est dans le cas où la tâche devrait tourner mais en réalité
|
|
||||||
* ce n'est pas le cas
|
|
||||||
*/
|
|
||||||
function _isUndead(?int $pid=null): bool {
|
|
||||||
$data = $this->read();
|
|
||||||
if ($data["date_start"] === null) return false;
|
|
||||||
if ($data["date_stop"] !== null) return false;
|
|
||||||
$pid ??= $data["pid"];
|
|
||||||
if (!posix_kill($pid, 0)) {
|
|
||||||
switch (posix_get_last_error()) {
|
|
||||||
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 3: #PCNTL_ESRCH:
|
|
||||||
# process inexistant
|
|
||||||
return true;
|
|
||||||
case 22: #PCNTL_EINVAL:
|
|
||||||
# ne devrait pas se produire
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# process existant auquel on a accès
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* comme {@link self::isStopped()} mais ne renvoie true qu'une seule fois si
|
|
||||||
* $updateDone==true
|
|
||||||
*/
|
|
||||||
function isDone(?array &$data=null, bool $updateDone=true): bool {
|
|
||||||
$done = false;
|
|
||||||
$this->update(function (array $ldata) use (&$done, &$data, $updateDone) {
|
|
||||||
$data = $ldata;
|
|
||||||
if ($data["date_start"] === null || $data["date_stop"] === null || $data["is_ack_done"]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$done = true;
|
|
||||||
if ($updateDone) return ["is_ack_done" => $done];
|
|
||||||
else return null;
|
|
||||||
});
|
|
||||||
return $done;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDesc(?array $data=null): ?string {
|
|
||||||
$data ??= $this->read();
|
|
||||||
$desc = $data["name"];
|
|
||||||
$dateStart = $data["date_start"];
|
|
||||||
$dateStop = $data["date_stop"];
|
|
||||||
$exitcode = $data["exitcode"];
|
|
||||||
if ($exitcode !== null) $exitcode = "\nCode de retour $exitcode";
|
|
||||||
if (!$this->wasStarted($data)) {
|
|
||||||
return "$desc: pas encore démarré";
|
|
||||||
} elseif ($this->isRunning($data)) {
|
|
||||||
$sinceStart = Elapsed::format_since($dateStart);
|
|
||||||
$started = "\nDémarré depuis $dateStart ($sinceStart)";
|
|
||||||
return "$desc: EN COURS pid $data[pid]$started";
|
|
||||||
} elseif ($this->isStopped($data)) {
|
|
||||||
$duration = "\nDurée ".Elapsed::format_delay($dateStart, $dateStop);
|
|
||||||
$sinceStop = Elapsed::format_since($dateStop);
|
|
||||||
$stopped = "\nArrêtée $sinceStop le $dateStop";
|
|
||||||
$reaped = $data["is_reaped"]? ", reaped": null;
|
|
||||||
$done = $data["is_ack_done"]? ", ACK done": null;
|
|
||||||
return "$desc: TERMINEE$duration$stopped$exitcode$reaped$done";
|
|
||||||
} else {
|
|
||||||
$stopped = $dateStop? "\nArrêtée le $dateStop": null;
|
|
||||||
return "$desc: CRASHED\nCommencé le $dateStart$stopped$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) {
|
|
||||||
return [
|
|
||||||
"action" => $title,
|
|
||||||
"action_date_start" => new DateTime(),
|
|
||||||
"action_max_step" => $maxSteps,
|
|
||||||
"action_current_step" => 0,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
app2::_dispatch_signals();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** indiquer qu'une étape est franchie dans l'action en cours */
|
|
||||||
function step(int $nbSteps=1): void {
|
|
||||||
$this->update(function (array $data) use ($nbSteps) {
|
|
||||||
return [
|
|
||||||
"action_date_step" => new DateTime(),
|
|
||||||
"action_current_step" => $data["action_current_step"] + $nbSteps,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
app2::_dispatch_signals();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getActionDesc(?array $data=null): ?string {
|
|
||||||
$data ??= $this->read();
|
|
||||||
$action = $data["action"];
|
|
||||||
if ($action !== null) {
|
|
||||||
$date ??= $data["action_date_step"];
|
|
||||||
$date ??= $data["action_date_start"];
|
|
||||||
if ($date !== null) $action = "$date $action";
|
|
||||||
$action = "Etape en cours: $action";
|
|
||||||
$current = $data["action_current_step"];
|
|
||||||
$max = $data["action_max_step"];
|
|
||||||
if ($current !== null && $max !== null) {
|
|
||||||
$action .= " ($current / $max)";
|
|
||||||
} elseif ($current !== null) {
|
|
||||||
$action .= " ($current)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $action;
|
|
||||||
}
|
|
||||||
|
|
||||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Divers
|
|
||||||
|
|
||||||
function getLockFile(?string $name=null, ?string $title=null): LockFile {
|
|
||||||
$ext = self::LOCK_EXT;
|
|
||||||
if ($name !== null) $ext = ".$name$ext";
|
|
||||||
$file = path::ensure_ext($this->file->getFile(), $ext, self::RUN_EXT);
|
|
||||||
$name = str::join("/", [$this->name, $name]);
|
|
||||||
return new LockFile($file, $name, $title);
|
|
||||||
}
|
|
||||||
}
|
|
366
wip/app/app.php
366
wip/app/app.php
|
@ -1,366 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace nur\sery\wip\app;
|
|
||||||
#XXX déplacer dans nur\sery\app dès que la dépendance sur nur\cli\Application sera levée
|
|
||||||
|
|
||||||
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
|
|
||||||
# my\package\MyApplication --> my-application
|
|
||||||
$name = preg_replace('/.*\\\\/', "", $name);
|
|
||||||
$name = str::without_suffix("-app", str::camel2us($name, false, "-"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param Application|string */
|
|
||||||
static function with($app, ?array $internal_use_params=null): self {
|
|
||||||
if ($app instanceof Application) {
|
|
||||||
$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)) {
|
|
||||||
$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)) {
|
|
||||||
$params = $app;
|
|
||||||
} else {
|
|
||||||
throw ValueException::invalid_type($app, Application::class);
|
|
||||||
}
|
|
||||||
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, ?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, 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 {
|
|
||||||
$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 = str_replace("-", "_", 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;
|
|
||||||
|
|
||||||
function getProjdir(): string {
|
|
||||||
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 {
|
|
||||||
return $this->appcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $apptype;
|
|
||||||
|
|
||||||
function getApptype(): string {
|
|
||||||
return $this->apptype;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $name;
|
|
||||||
|
|
||||||
function getName(): ?string {
|
|
||||||
return $this->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ?string $title;
|
|
||||||
|
|
||||||
function getTitle(): ?string {
|
|
||||||
return $this->title;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $profile;
|
|
||||||
|
|
||||||
function getProfile(): string {
|
|
||||||
return $this->profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ?string|false $profile
|
|
||||||
*/
|
|
||||||
function withProfile(string $file, $profile): string {
|
|
||||||
if ($profile !== false) {
|
|
||||||
if ($profile === null) $profile = $this->getProfile();
|
|
||||||
[$dir, $filename] = path::split($file);
|
|
||||||
$basename = path::basename($filename);
|
|
||||||
$ext = path::ext($file);
|
|
||||||
$file = path::join($dir, "$basename.$profile$ext");
|
|
||||||
}
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findFile(array $dirs, array $names, $profile=null): string {
|
|
||||||
# d'abord chercher avec le profil
|
|
||||||
if ($profile !== false) {
|
|
||||||
foreach ($dirs as $dir) {
|
|
||||||
foreach ($names as $name) {
|
|
||||||
$file = path::join($dir, $name);
|
|
||||||
$file = $this->withProfile($file, $profile);
|
|
||||||
if (file_exists($file)) return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# puis sans profil
|
|
||||||
foreach ($dirs as $dir) {
|
|
||||||
foreach ($names as $name) {
|
|
||||||
$file = path::join($dir, $name);
|
|
||||||
if (file_exists($file)) return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# la valeur par défaut est avec profil
|
|
||||||
return $this->withProfile(path::join($dirs[0], $names[0]), $profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fencedJoin(string $basedir, string $path): string {
|
|
||||||
$path = path::reljoin($basedir, $path);
|
|
||||||
if (!path::is_within($path, $basedir)) {
|
|
||||||
throw ValueException::invalid_value($path, "path");
|
|
||||||
}
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $cwd;
|
|
||||||
|
|
||||||
function getCwd(): string {
|
|
||||||
return $this->cwd;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $datadir;
|
|
||||||
|
|
||||||
function getDatadir(): string {
|
|
||||||
return $this->datadir;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $etcdir;
|
|
||||||
|
|
||||||
function getEtcdir(): string {
|
|
||||||
return $this->etcdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEtcfile(string $name, $profile=null): string {
|
|
||||||
return $this->findFile([$this->etcdir], [$name], $profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $vardir;
|
|
||||||
|
|
||||||
function getVardir(): string {
|
|
||||||
return $this->vardir;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVarfile(string $name, $profile=null): string {
|
|
||||||
$file = $this->withProfile($this->fencedJoin($this->vardir, $name), $profile);
|
|
||||||
sh::mkdirof($file);
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string $logdir;
|
|
||||||
|
|
||||||
function getLogdir(): string {
|
|
||||||
return $this->logdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLogfile(?string $name=null, $profile=null): string {
|
|
||||||
if ($name === null) $name = "{$this->name}.log";
|
|
||||||
$file = $this->withProfile($this->fencedJoin($this->logdir, $name), $profile);
|
|
||||||
sh::mkdirof($file);
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* obtenir le chemin absolu vers un fichier de travail
|
|
||||||
* - si le chemin est absolu, il est inchangé
|
|
||||||
* - si le chemin est qualifié (commence par ./ ou ../) ou sans chemin, il est
|
|
||||||
* exprimé par rapport à $vardir
|
|
||||||
* - sinon le chemin est exprimé par rapport au répertoire de travail de base
|
|
||||||
* $datadir
|
|
||||||
*
|
|
||||||
* is $ensure_dir, créer le répertoire du fichier s'il n'existe pas déjà
|
|
||||||
*/
|
|
||||||
function getWorkfile(?string $file, $profile=null, bool $ensureDir=true): ?string {
|
|
||||||
if ($file === null) return null;
|
|
||||||
if (path::is_qualified($file) || !path::have_dir($file)) {
|
|
||||||
$file = path::reljoin($this->vardir, $file);
|
|
||||||
} else {
|
|
||||||
$file = path::reljoin($this->datadir, $file);
|
|
||||||
}
|
|
||||||
$file = $this->withProfile($file, $profile);
|
|
||||||
if ($ensureDir) sh::mkdirof($file);
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* obtenir le chemin absolu vers un fichier spécifié par l'utilisateur.
|
|
||||||
* - si le chemin commence par /, il est laissé en l'état
|
|
||||||
* - si le chemin commence par ./ ou ../, il est exprimé par rapport à $cwd
|
|
||||||
* - sinon le chemin est exprimé par rapport au répertoire de travail $vardir
|
|
||||||
*/
|
|
||||||
function getUserfile(?string $file): ?string {
|
|
||||||
if ($file === null) return null;
|
|
||||||
if (path::is_qualified($file)) {
|
|
||||||
return path::reljoin($this->cwd, $file);
|
|
||||||
} else {
|
|
||||||
return path::reljoin($this->vardir, $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ?RunFile $runfile = null;
|
|
||||||
|
|
||||||
function getRunfile(): RunFile {
|
|
||||||
$name = $this->name;
|
|
||||||
$runfile = $this->getWorkfile($name);
|
|
||||||
$logfile = $this->getLogfile();
|
|
||||||
return $this->runfile ??= new RunFile($name, $runfile, $logfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ?array $lockFiles = null;
|
|
||||||
|
|
||||||
function getLockfile(?string $name=null): LockFile {
|
|
||||||
$this->lockFiles[$name] ??= $this->getRunfile()->getLockFile($name, $this->title);
|
|
||||||
return $this->lockFiles[$name];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,576 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace nur\sery\wip\os\proc;
|
|
||||||
|
|
||||||
use nur\sery\php\coll\AutoArray;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ManagedTask: une tâche de fond
|
|
||||||
*
|
|
||||||
* --autogen-properties-and-methods--
|
|
||||||
*/
|
|
||||||
class ManagedTask extends AutoArray {
|
|
||||||
const LOCK = "task";
|
|
||||||
|
|
||||||
const DEFINITION_SCHEMA = [
|
|
||||||
"id" => ["string", null, "identifiant de la tâche"],
|
|
||||||
"serial" => ["int", null, "numéro de série permettant de distinguer deux occurrences de la tâche"],
|
|
||||||
"title" => ["?string", null, "description de la tâche"],
|
|
||||||
"valid" => ["bool", false, "la tâche est-elle valide?"],
|
|
||||||
"owner_login" => ["?string", null, "compte de la personne qui a lancé la tâche"],
|
|
||||||
"owner_name" => ["?string", null, "nom de la personne qui a lancé la tâche"],
|
|
||||||
"owner_page" => ["?string", null, "page qui a créé cette tâche"],
|
|
||||||
"owner_params" => ["?array", null, "paramètres à passer à la page"],
|
|
||||||
"cmd" => [null, null, "commande à lancer"],
|
|
||||||
"logfile" => ["?string", null, "sortie de la commande"],
|
|
||||||
];
|
|
||||||
|
|
||||||
const SCHEMA = [
|
|
||||||
"definition" => [
|
|
||||||
"?array", null, "définition de la tâche",
|
|
||||||
"schema" => self::DEFINITION_SCHEMA,
|
|
||||||
],
|
|
||||||
"state" => [
|
|
||||||
"?array", null, "instance de la tâche",
|
|
||||||
"schema" => [
|
|
||||||
"definition" => [
|
|
||||||
"array", null, "copie de la définition de la tâche",
|
|
||||||
"schema" => self::DEFINITION_SCHEMA,
|
|
||||||
],
|
|
||||||
"started" => ["bool", false, "la tâche a-t-elle été démarrée?"],
|
|
||||||
"date_start" => ["?datetime", null, "date du démarrage de la tâche"],
|
|
||||||
"pid" => ["?int", null, "PID du process contrôleur"],
|
|
||||||
"status" => ["?string", null, "Message de statut indiqué par la tâche"],
|
|
||||||
"stopped" => ["bool", false, "la tâche est-elle terminée?"],
|
|
||||||
"date_stop" => ["?datetime", null, "date de l'arrêt de la tâche"],
|
|
||||||
"retcode" => ["?int", null, "code de retour de la commande"],
|
|
||||||
"done" => ["bool", false, "la fin de la tâche a-t-elle été prise en compte?"],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"" => [
|
|
||||||
"auto_properties" => [
|
|
||||||
"id" => "definition.id",
|
|
||||||
"serial" => "definition.serial",
|
|
||||||
"title" => "definition.title",
|
|
||||||
"valid" => "definition.valid",
|
|
||||||
"owner_login" => "definition.owner_login",
|
|
||||||
"owner_name" => "definition.owner_name",
|
|
||||||
"owner_page" => "definition.owner_page",
|
|
||||||
"owner_params" => "definition.owner_params",
|
|
||||||
"cmd" => "definition.cmd",
|
|
||||||
"logfile" => "definition.logfile",
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
const _AUTO_PROPERTIES = self::SCHEMA[""]["auto_properties"];
|
|
||||||
|
|
||||||
const _SCHEMA = [
|
|
||||||
"id" => ["string", null, "identifiant de la tâche"],
|
|
||||||
"serial" => ["string", null, "numéro de série permettant de distinguer deux occurrences de la tâche"],
|
|
||||||
"title" => ["?string", null, "description de la tâche"],
|
|
||||||
"valid" => ["bool", false, "la tâche est-elle valide?"],
|
|
||||||
"owner_login" => ["?string", null, "compte de la personne qui a lancé la tâche"],
|
|
||||||
"owner_name" => ["?string", null, "nom de la personne qui a lancé la tâche"],
|
|
||||||
"page" => ["?array", null, "page qui a créé cette tâche et paramètres à passer à la page"],
|
|
||||||
"cmd" => [null, null, "commande à lancer"],
|
|
||||||
"logfile" => ["?string", null, "sortie de commande"],
|
|
||||||
"started" => ["bool", false, "la tâche a-t-elle été démarrée?"],
|
|
||||||
"date_start" => ["?datetime", null, "date du démarrage de la tâche"],
|
|
||||||
"pid" => ["?int", null, "PID du process contrôleur"],
|
|
||||||
"stopped" => ["bool", false, "la tâche est-elle terminée?"],
|
|
||||||
"date_stop" => ["?datetime", null, "date de l'arrêt de la tâche"],
|
|
||||||
"retcode" => ["?int", null, "code de retour de la commande"],
|
|
||||||
"done" => ["bool", false, "la fin de la tâche a-t-elle été prise en compte?"],
|
|
||||||
];
|
|
||||||
|
|
||||||
function __construct(string $id, bool $autoUpdate=false, ?callable $init=null) {
|
|
||||||
# ne pas appeler parent::__construct()
|
|
||||||
if (file_exists($id)) {
|
|
||||||
$file = $id;
|
|
||||||
} else {
|
|
||||||
$authz = authz::get();
|
|
||||||
$this->data = [
|
|
||||||
"id" => $id,
|
|
||||||
"serial" => 0,
|
|
||||||
"owner_login" => $authz->getUsername(),
|
|
||||||
"owner_name" => $authz->getDisplayName(),
|
|
||||||
];
|
|
||||||
$file = tasks::pf("$id.task");
|
|
||||||
}
|
|
||||||
$this->init = $init;
|
|
||||||
$this->file = $file;
|
|
||||||
$this->ensureTask($autoUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var ?callable */
|
|
||||||
protected $init;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
protected $file;
|
|
||||||
|
|
||||||
private function ensureTask(bool $autoUpdate): void {
|
|
||||||
lock::exlusive(self::LOCK);
|
|
||||||
try {
|
|
||||||
if (is_file($this->file)) {
|
|
||||||
$this->_reload();
|
|
||||||
} else {
|
|
||||||
$this->_reset();
|
|
||||||
$this->_save();
|
|
||||||
}
|
|
||||||
$logfile = $this->getLogfile();
|
|
||||||
if ($logfile !== null) os::mkdirof($logfile);
|
|
||||||
} finally {
|
|
||||||
lock::release(self::LOCK);
|
|
||||||
}
|
|
||||||
if ($autoUpdate) $this->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _init(): bool {
|
|
||||||
$init = $this->init;
|
|
||||||
if ($init !== null) {
|
|
||||||
func::call($init, $this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function init(): void {
|
|
||||||
if ($this->init !== null) {
|
|
||||||
lock::exlusive(self::LOCK);
|
|
||||||
try {
|
|
||||||
$this->_init();
|
|
||||||
$this->_save();
|
|
||||||
} finally {
|
|
||||||
lock::release(self::LOCK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _reset(): void {
|
|
||||||
$authz = authz::get();
|
|
||||||
$id = $this->data["id"];
|
|
||||||
$serial = A::get($this->data, "serial", 0);
|
|
||||||
$this->data = $this->ensureData([
|
|
||||||
"id" => $id,
|
|
||||||
"serial" => $serial + 1,
|
|
||||||
"owner_login" => $authz->getUsername(),
|
|
||||||
"owner_name" => $authz->getDisplayName(),
|
|
||||||
"logfile" => logs::pf("$id/latest.log"),
|
|
||||||
]);
|
|
||||||
$this->_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset(): void {
|
|
||||||
lock::exlusive(self::LOCK);
|
|
||||||
try {
|
|
||||||
$this->_reset();
|
|
||||||
$this->_save();
|
|
||||||
} finally {
|
|
||||||
lock::release(self::LOCK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _reload(): void {
|
|
||||||
$this->data = unserialize(file_get_contents($this->file));
|
|
||||||
}
|
|
||||||
|
|
||||||
function reload(): void {
|
|
||||||
lock::exlusive(self::LOCK);
|
|
||||||
try {
|
|
||||||
$this->_reload();
|
|
||||||
} finally {
|
|
||||||
lock::release(self::LOCK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _save(): void {
|
|
||||||
os::mkdirof($this->file);
|
|
||||||
$outf = fopen($this->file, "w+");
|
|
||||||
fwrite($outf, serialize($this->data));
|
|
||||||
fclose($outf);
|
|
||||||
}
|
|
||||||
|
|
||||||
function save(): void {
|
|
||||||
lock::exlusive(self::LOCK);
|
|
||||||
try {
|
|
||||||
$this->_save();
|
|
||||||
} finally {
|
|
||||||
lock::release(self::LOCK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** vérifier que l'objet est bien initialisé */
|
|
||||||
function validate(): void {
|
|
||||||
if (!$this->isValid()) {
|
|
||||||
if ($this->getCmd() === null) {
|
|
||||||
throw new ValueException("cmd is required");
|
|
||||||
}
|
|
||||||
$this->setValid(true);
|
|
||||||
$this->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _launch(): void {
|
|
||||||
$args = [
|
|
||||||
__DIR__.'/../../lib/launch_task.php',
|
|
||||||
"--envname", envs::get(),
|
|
||||||
$this->getId(),
|
|
||||||
];
|
|
||||||
$logfile = $this->getLogfile();
|
|
||||||
if ($logfile !== null) A::merge($args, ["--logfile", $logfile]);
|
|
||||||
$cmd = new Cmd($args);
|
|
||||||
$cmd->addRedir("null");
|
|
||||||
$cmd->passthru();
|
|
||||||
$this->reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLaunchable(): bool {
|
|
||||||
return $this->isStarted();
|
|
||||||
}
|
|
||||||
|
|
||||||
function launch(): void {
|
|
||||||
if (!$this->isStartable()) return;
|
|
||||||
if ($this->isDone()) $this->reset();
|
|
||||||
$this->_launch();
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUpdatable(): bool {
|
|
||||||
return $this->isLaunchable() && !$this->isDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
function update(): void {
|
|
||||||
if ($this->isUpdatable()) $this->_launch();
|
|
||||||
else $this->init();
|
|
||||||
}
|
|
||||||
|
|
||||||
function kill(): void {
|
|
||||||
if (!$this->isStarted() || $this->isStopped()) return;
|
|
||||||
$args = [
|
|
||||||
__DIR__.'/../../lib/launch_task.php',
|
|
||||||
"-e", envs::get(),
|
|
||||||
"--kill",
|
|
||||||
$this->getId(),
|
|
||||||
];
|
|
||||||
$logfile = $this->getLogfile();
|
|
||||||
if ($logfile !== null) A::merge($args, ["-L", $logfile]);
|
|
||||||
$cmd = new Cmd($args);
|
|
||||||
$cmd->addRedir("null");
|
|
||||||
$cmd->passthru();
|
|
||||||
$this->reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function isStartable(): bool {
|
|
||||||
return !$this->isStarted() || $this->isDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* démarrer la commande. doit être lancé depuis launch_task.php
|
|
||||||
*/
|
|
||||||
function ltStart(?string $logfile): void {
|
|
||||||
$pid = pcntl_fork();
|
|
||||||
if ($pid == -1) {
|
|
||||||
# parent, impossible de forker
|
|
||||||
throw new IllegalAccessException("unable to fork");
|
|
||||||
} elseif ($pid) {
|
|
||||||
# parent, fork ok
|
|
||||||
$this->setStarted(true);
|
|
||||||
$this->setDateStart(date::datetime());
|
|
||||||
$this->setPid($pid);
|
|
||||||
$this->save();
|
|
||||||
} else {
|
|
||||||
## child, fork ok
|
|
||||||
# Créer un groupe de process, pour pouvoir les tuer toutes en même temps
|
|
||||||
posix_setsid();
|
|
||||||
msg::push($oldMsg, null, [
|
|
||||||
"output" => $logfile,
|
|
||||||
]);
|
|
||||||
$retcode = -776;
|
|
||||||
try {
|
|
||||||
# tout d'abord synchroniser les fichiers le cas échéant
|
|
||||||
$command = $this->get("command");
|
|
||||||
$append = false;
|
|
||||||
if ($command !== null) {
|
|
||||||
$files = $command["files"];
|
|
||||||
$forceSync = $this->get("force_sync");
|
|
||||||
files::sync($files, $forceSync, $logfile, "wb");
|
|
||||||
$append = true;
|
|
||||||
}
|
|
||||||
# puis lancer la commande
|
|
||||||
$cmd = Cmd::with($this->getCmd());
|
|
||||||
if ($logfile !== null) $cmd->addRedir("both", $logfile, $append);
|
|
||||||
$cmd->fork_exec($retcode);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
msg::error($e);
|
|
||||||
} finally {
|
|
||||||
$this->reload();
|
|
||||||
$this->setStopped(true);
|
|
||||||
$this->setDateStop(date::datetime());
|
|
||||||
$this->setRetcode($retcode);
|
|
||||||
$this->save();
|
|
||||||
msg::pop($oldMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** arrêter la commande. doit être lancé depuis launch_task.php */
|
|
||||||
function ltKill(?string $logfile): void {
|
|
||||||
msg::push($oldMsg, null, $logfile);
|
|
||||||
try {
|
|
||||||
$id = $this->getId();
|
|
||||||
$pid = $this->getPid();
|
|
||||||
msg::action("$id: $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 ($this->ltIsUndead()) {
|
|
||||||
sleep(1);
|
|
||||||
if (--$timeout == 0) {
|
|
||||||
msg::afailure("tentative d'arrêt de la tâche");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg::asuccess("tâche arrêtée");
|
|
||||||
$this->setStopped(true);
|
|
||||||
$this->setDateStop(date::datetime());
|
|
||||||
$this->setRetcode(-787);
|
|
||||||
$this->setDone(true);
|
|
||||||
$this->save();
|
|
||||||
} finally {
|
|
||||||
msg::pop($oldMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isReapable(): bool {
|
|
||||||
return $this->isStopped() && !$this->isDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* marquer la commande comme terminée. doit être lancé depuis launch_task.php
|
|
||||||
*/
|
|
||||||
function ltReap(): void {
|
|
||||||
pcntl_waitpid($this->getPid(), $status);
|
|
||||||
$this->setDone(true);
|
|
||||||
$this->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* vérifier si on est dans le cas où la tâche est censée tourner mais en
|
|
||||||
* réalité ce n'est pas le cas. doit être lancé depuis launch_task.php
|
|
||||||
*/
|
|
||||||
function ltIsUndead(): bool {
|
|
||||||
if (!posix_kill($this->getPid(), 0)) {
|
|
||||||
switch (posix_get_last_error()) {
|
|
||||||
case PCNTL_ESRCH:
|
|
||||||
# process inexistant
|
|
||||||
return true;
|
|
||||||
case PCNTL_EPERM:
|
|
||||||
# process auquel on n'a pas accès: ce doit être un autre process qui a
|
|
||||||
# réutilisé le PID
|
|
||||||
return true;
|
|
||||||
case PCNTL_EINVAL:
|
|
||||||
# ne devrait pas se produire
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# process existant
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* marquer la tâche comme terminée avec un code d'erreur si elle n'existe
|
|
||||||
* plus. doit être lancé depuis launch_task.php
|
|
||||||
*/
|
|
||||||
function ltCleanUndead(): void {
|
|
||||||
if (!$this->isStopped()) {
|
|
||||||
$this->setStopped(true);
|
|
||||||
$this->setDateStop(date::datetime());
|
|
||||||
$this->setRetcode(-777);
|
|
||||||
}
|
|
||||||
$this->setDone(true);
|
|
||||||
$this->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIdTitle(): string {
|
|
||||||
$idTitle = $this->getId();
|
|
||||||
$title = $this->getTitle();
|
|
||||||
if ($title) $idTitle .= " -- $title";
|
|
||||||
return $idTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNameOrLogin(): string {
|
|
||||||
$nameOrLogin = $this->getOwnerName();
|
|
||||||
if ($nameOrLogin === null) $nameOrLogin = $this->getOwnerLogin();
|
|
||||||
if ($nameOrLogin === null) $nameOrLogin = "(unknown)";
|
|
||||||
return $nameOrLogin;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_LOG_SIZE = 256 * 1024;
|
|
||||||
const CACTION_NONE = "n";
|
|
||||||
const CACTION_REPLACE = "r";
|
|
||||||
const CACTION_UPDATE = "u";
|
|
||||||
|
|
||||||
function export(?int $serial=null, ?int $cs=null, ?int $ce=null): array {
|
|
||||||
$task = $this->array();
|
|
||||||
$dateStart = new Datetime($this->getDateStart());
|
|
||||||
$dateStop = new Datetime($this->getDateStop());
|
|
||||||
# $ca = action à faire par le client: replace ou update
|
|
||||||
# ls = local start, le = local end (local === server en l'occurrence)
|
|
||||||
# cs = client start, ce = client end (pour CACTION_REPLACE)
|
|
||||||
# $ps = plus start, $pe = plus end (pour CACTION_UPDATE)
|
|
||||||
# $rs = read start, $re = read end
|
|
||||||
if ($serial !== null && $cs !== null && $ce !== null && $this->isStarted()) {
|
|
||||||
lock::exlusive(self::LOCK);
|
|
||||||
$inf = false;
|
|
||||||
try {
|
|
||||||
$logfile = $this->getLogfile();
|
|
||||||
if (!file_exists($logfile)) {
|
|
||||||
# s'assurer que le fichier existe (il peut avoir été nettoyé entre temps)
|
|
||||||
f::close(f::open($logfile, "cb"));
|
|
||||||
}
|
|
||||||
$inf = f::open($logfile, "rb");
|
|
||||||
$le = f::seek($inf, 0, SEEK_END);
|
|
||||||
$ls = $le - self::MAX_LOG_SIZE;
|
|
||||||
if ($ls <= 0) {
|
|
||||||
$ls = 0;
|
|
||||||
} else {
|
|
||||||
# trouver le premier saut de ligne
|
|
||||||
$ls = f::find_nl($inf, $ls);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($serial != $this->getSerial()) {
|
|
||||||
# nouvelle tâche, on recommence tout
|
|
||||||
$rs = $cs = $ls;
|
|
||||||
$re = $ce = $le;
|
|
||||||
$ca = self::CACTION_REPLACE;
|
|
||||||
} elseif ($ls <= $cs) {
|
|
||||||
# cas courant, on rajoute du contenu, mais pas plus que MAX_LOG_SIZE
|
|
||||||
$ls = $cs;
|
|
||||||
$ps = 0;
|
|
||||||
$pe = $le - $ce;
|
|
||||||
$rs = $ce;
|
|
||||||
$re = $le;
|
|
||||||
$ca = self::CACTION_UPDATE;
|
|
||||||
$cs = $ps;
|
|
||||||
$ce = $pe;
|
|
||||||
} elseif ($ls <= $ce) {
|
|
||||||
# on a dépassé MAX_LOG_SIZE, il faut recalculer
|
|
||||||
# garder une partie des logs précédents
|
|
||||||
$ps = $ls - $cs;
|
|
||||||
$pe = $le - $ce;
|
|
||||||
$rs = $ce;
|
|
||||||
$re = $le;
|
|
||||||
$ca = self::CACTION_UPDATE;
|
|
||||||
$cs = $ps;
|
|
||||||
$ce = $pe;
|
|
||||||
} else {
|
|
||||||
# ne rien garder des logs précédents
|
|
||||||
$rs = $cs = $ls;
|
|
||||||
$re = $ce = $le;
|
|
||||||
$ca = self::CACTION_REPLACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
$logSize = $re - $rs;
|
|
||||||
if ($logSize > 0) {
|
|
||||||
f::seek($inf, $rs, SEEK_SET);
|
|
||||||
$log = f::read($inf, $logSize);
|
|
||||||
$lf = new BaseF(); #XXX
|
|
||||||
$lf->formatContent($log);
|
|
||||||
} elseif ($ca == self::CACTION_REPLACE) {
|
|
||||||
$log = "";
|
|
||||||
} else {
|
|
||||||
$log = false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if ($inf) f::close($inf);
|
|
||||||
lock::release(self::LOCK);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$cs = $ce = false;
|
|
||||||
$ca = self::CACTION_NONE;
|
|
||||||
$log = false;
|
|
||||||
}
|
|
||||||
$page = $this->getPage();
|
|
||||||
if ($page !== null) {
|
|
||||||
$dest = A::get($page, 0);
|
|
||||||
$params = A::get($page, 1);
|
|
||||||
$pageUrl = page::bu($dest, $params);
|
|
||||||
} else {
|
|
||||||
$pageUrl = false;
|
|
||||||
}
|
|
||||||
A::merge($task, [
|
|
||||||
"id_title" => $this->getIdTitle(),
|
|
||||||
"name_or_login" => $this->getNameOrLogin(),
|
|
||||||
"page_url" => $pageUrl,
|
|
||||||
"elapsed_start" => $dateStart->getElapsed()->formatAt(),
|
|
||||||
"elapsed_stop" => $dateStop->getElapsed()->formatSince(),
|
|
||||||
"elapsed_total" => $dateStart->getElapsed($dateStop)->formatDelay(),
|
|
||||||
"launchable" => $this->isLaunchable(),
|
|
||||||
"updatable" => $this->isUpdatable(),
|
|
||||||
"startable" => $this->isStartable(),
|
|
||||||
"reapable" => $this->isReapable(),
|
|
||||||
"working" => $this->isStarted() && !$this->isDone(),
|
|
||||||
"ok" => $this->isDone() && $this->getRetcode() == 0,
|
|
||||||
"ko" => $this->isDone() && $this->getRetcode() != 0,
|
|
||||||
"log" => $log,
|
|
||||||
]);
|
|
||||||
return [$task, $ca, $cs, $ce];
|
|
||||||
}
|
|
||||||
|
|
||||||
#############################################################################
|
|
||||||
const _AUTOGEN_CONSTS = [
|
|
||||||
"_AUTO_GETTERS" => [Autogen::class, "auto_getters", self::SCHEMA],
|
|
||||||
"_AUTO_SETTERS" => [Autogen::class, "auto_setters", self::SCHEMA],
|
|
||||||
];
|
|
||||||
const _AUTOGEN_METHODS = [
|
|
||||||
[Autogen::class, "auto_getters_methods", self::SCHEMA],
|
|
||||||
[Autogen::class, "auto_setters_methods", self::SCHEMA],
|
|
||||||
];
|
|
||||||
const _AUTO_GETTERS = /*autogen*/[
|
|
||||||
'getId' => 'id',
|
|
||||||
'getSerial' => 'serial',
|
|
||||||
'getTitle' => 'title',
|
|
||||||
'isValid' => 'valid',
|
|
||||||
'getOwnerLogin' => 'owner_login',
|
|
||||||
'getOwnerName' => 'owner_name',
|
|
||||||
'getPage' => 'page',
|
|
||||||
'getCmd' => 'cmd',
|
|
||||||
'getLogfile' => 'logfile',
|
|
||||||
'isStarted' => 'started',
|
|
||||||
'getDateStart' => 'date_start',
|
|
||||||
'getPid' => 'pid',
|
|
||||||
'isStopped' => 'stopped',
|
|
||||||
'getDateStop' => 'date_stop',
|
|
||||||
'getRetcode' => 'retcode',
|
|
||||||
'isDone' => 'done',
|
|
||||||
];
|
|
||||||
const _AUTO_SETTERS = /*autogen*/[
|
|
||||||
'setId' => 'id',
|
|
||||||
'setSerial' => 'serial',
|
|
||||||
'setTitle' => 'title',
|
|
||||||
'setValid' => 'valid',
|
|
||||||
'setOwnerLogin' => 'owner_login',
|
|
||||||
'setOwnerName' => 'owner_name',
|
|
||||||
'setPage' => 'page',
|
|
||||||
'setCmd' => 'cmd',
|
|
||||||
'setLogfile' => 'logfile',
|
|
||||||
'setStarted' => 'started',
|
|
||||||
'setDateStart' => 'date_start',
|
|
||||||
'setPid' => 'pid',
|
|
||||||
'setStopped' => 'stopped',
|
|
||||||
'setDateStop' => 'date_stop',
|
|
||||||
'setRetcode' => 'retcode',
|
|
||||||
'setDone' => 'done',
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace nur\sery\wip\os\proc;
|
|
||||||
|
|
||||||
class tasks {
|
|
||||||
static function pf(string $name): string {
|
|
||||||
$envname = envs::get();
|
|
||||||
return "/tasks/$envname/$name";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** le verrou doit être posé avant l'appel de cette méthode */
|
|
||||||
private static function _list(bool $include_invalids, bool $sort): array {
|
|
||||||
$tmpfiles = glob(self::pf("*.task"));
|
|
||||||
if ($tmpfiles === false) return [];
|
|
||||||
$tasks = [];
|
|
||||||
foreach ($tmpfiles as $taskfile) {
|
|
||||||
$task = new ManagedTask($taskfile);
|
|
||||||
if (!$include_invalids && !$task->isValid()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$tasks[] = [$taskfile, $task];
|
|
||||||
}
|
|
||||||
if ($sort) {
|
|
||||||
clearstatcache();
|
|
||||||
usort($tasks, function ($fta, $ftb) {
|
|
||||||
/**
|
|
||||||
* @var ManagedTask $ta
|
|
||||||
* @var ManagedTask $tb
|
|
||||||
*/
|
|
||||||
[$fa, $ta] = $fta;
|
|
||||||
[$fb, $tb] = $ftb;
|
|
||||||
# comparer l'état "running"
|
|
||||||
$wa = $ta->isStarted() && !$ta->isDone();
|
|
||||||
$wb = $tb->isStarted() && !$tb->isDone();
|
|
||||||
$c = -base::compare($wa, $wb);
|
|
||||||
if ($c != 0) return $c;
|
|
||||||
# comparer la date de dernière modification du fichier
|
|
||||||
$mta = filemtime($fa);
|
|
||||||
$mtb = filemtime($fb);
|
|
||||||
return -base::compare($mta, $mtb);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return $tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** supprimer toutes les tâches */
|
|
||||||
static function delete_all(): void {
|
|
||||||
lock::exlusive(ManagedTask::LOCK);
|
|
||||||
try {
|
|
||||||
foreach (self::_list(true, false) as [$taskfile, $task]) {
|
|
||||||
unlink($taskfile);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock::release(ManagedTask::LOCK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* retourner la liste des tâches valides
|
|
||||||
* @return ManagedTask[]
|
|
||||||
*/
|
|
||||||
static function list(?string $selectId=null): array {
|
|
||||||
$tasks = [];
|
|
||||||
lock::exlusive(ManagedTask::LOCK);
|
|
||||||
try {
|
|
||||||
foreach (self::_list(false, true) as [$taskfile, $task]) {
|
|
||||||
$id = $task->getId();
|
|
||||||
if ($selectId !== null && $id !== $selectId) continue;
|
|
||||||
$tasks[$id] = $task;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock::release(ManagedTask::LOCK);
|
|
||||||
}
|
|
||||||
return $tasks;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\app\cli;
|
namespace nur\sery\app\cli;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use nur\cli\ArgsException;
|
use nur\cli\ArgsException;
|
||||||
use nur\cli\ArgsParser;
|
use nur\cli\ArgsParser;
|
||||||
use nur\config;
|
use nur\config;
|
||||||
|
use nur\sery\app\app;
|
||||||
|
use nur\sery\app\RunFile;
|
||||||
use nur\sery\ExitError;
|
use nur\sery\ExitError;
|
||||||
use nur\sery\output\console;
|
use nur\sery\output\console;
|
||||||
use nur\sery\output\log;
|
use nur\sery\output\log;
|
||||||
use nur\sery\output\msg;
|
use nur\sery\output\msg;
|
||||||
use nur\sery\output\std\StdMessenger;
|
use nur\sery\output\std\StdMessenger;
|
||||||
use nur\sery\ValueException;
|
use nur\sery\ValueException;
|
||||||
use nur\sery\wip\app\app2;
|
|
||||||
use nur\sery\wip\app\RunFile;
|
|
||||||
use nur\sery\wip\web\content\v;
|
|
||||||
use nur\yaml;
|
use nur\yaml;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,7 +144,7 @@ EOT);
|
||||||
else $ec = self::_error("not running");
|
else $ec = self::_error("not running");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$ec = self::_error("$argv[1]: unexpected command", app2::EC_BAD_COMMAND);
|
$ec = self::_error("$argv[1]: unexpected command", app::EC_BAD_COMMAND);
|
||||||
}
|
}
|
||||||
exit($ec);
|
exit($ec);
|
||||||
}
|
}
|
||||||
|
@ -155,26 +154,26 @@ EOT);
|
||||||
$stop = false;
|
$stop = false;
|
||||||
$shutdown = function () use (&$unlock, &$stop) {
|
$shutdown = function () use (&$unlock, &$stop) {
|
||||||
if ($unlock) {
|
if ($unlock) {
|
||||||
app2::get()->getRunfile()->release();
|
app::get()->getRunfile()->release();
|
||||||
$unlock = false;
|
$unlock = false;
|
||||||
}
|
}
|
||||||
if ($stop) {
|
if ($stop) {
|
||||||
app2::get()->getRunfile()->wfStop();
|
app::get()->getRunfile()->wfStop();
|
||||||
$stop = false;
|
$stop = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
register_shutdown_function($shutdown);
|
register_shutdown_function($shutdown);
|
||||||
app2::install_signal_handler(static::INSTALL_SIGNAL_HANDLER);
|
app::install_signal_handler(static::INSTALL_SIGNAL_HANDLER);
|
||||||
try {
|
try {
|
||||||
static::_initialize_app();
|
static::_initialize_app();
|
||||||
$useRunfile = static::USE_RUNFILE;
|
$useRunfile = static::USE_RUNFILE;
|
||||||
$useRunlock = static::USE_RUNLOCK;
|
$useRunlock = static::USE_RUNLOCK;
|
||||||
if ($useRunfile) {
|
if ($useRunfile) {
|
||||||
$runfile = app2::get()->getRunfile();
|
$runfile = app::get()->getRunfile();
|
||||||
|
|
||||||
global $argc, $argv;
|
global $argc, $argv;
|
||||||
self::_manage_runfile($argc, $argv, $runfile);
|
self::_manage_runfile($argc, $argv, $runfile);
|
||||||
if ($useRunlock && $runfile->warnIfLocked()) exit(app2::EC_LOCKED);
|
if ($useRunlock && $runfile->warnIfLocked()) exit(app::EC_LOCKED);
|
||||||
|
|
||||||
$runfile->wfStart();
|
$runfile->wfStart();
|
||||||
$stop = true;
|
$stop = true;
|
||||||
|
@ -191,12 +190,12 @@ EOT);
|
||||||
exit($e->getCode());
|
exit($e->getCode());
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
msg::error($e);
|
msg::error($e);
|
||||||
exit(app2::EC_UNEXPECTED);
|
exit(app::EC_UNEXPECTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function _initialize_app(): void {
|
protected static function _initialize_app(): void {
|
||||||
app2::init(static::class);
|
app::init(static::class);
|
||||||
msg::set_messenger(new StdMessenger([
|
msg::set_messenger(new StdMessenger([
|
||||||
"min_level" => msg::DEBUG,
|
"min_level" => msg::DEBUG,
|
||||||
]));
|
]));
|
||||||
|
@ -211,7 +210,7 @@ EOT);
|
||||||
]);
|
]);
|
||||||
if (static::USE_LOGFILE) {
|
if (static::USE_LOGFILE) {
|
||||||
$msgs["log"] = new StdMessenger([
|
$msgs["log"] = new StdMessenger([
|
||||||
"output" => app2::get()->getLogfile(),
|
"output" => app::get()->getLogfile(),
|
||||||
"min_level" => msg::MINOR,
|
"min_level" => msg::MINOR,
|
||||||
"add_date" => true,
|
"add_date" => true,
|
||||||
]);
|
]);
|
||||||
|
@ -254,12 +253,12 @@ EOT);
|
||||||
["group",
|
["group",
|
||||||
["-p", "--profile", "--app-profile",
|
["-p", "--profile", "--app-profile",
|
||||||
"args" => 1, "argsdesc" => "PROFILE",
|
"args" => 1, "argsdesc" => "PROFILE",
|
||||||
"action" => [app2::class, "set_profile"],
|
"action" => [app::class, "set_profile"],
|
||||||
"help" => "spécifier le profil d'exécution",
|
"help" => "spécifier le profil d'exécution",
|
||||||
],
|
],
|
||||||
["-P", "--prod", "action" => [app2::class, "set_profile", config::PROD]],
|
["-P", "--prod", "action" => [app::class, "set_profile", config::PROD]],
|
||||||
["-T", "--test", "action" => [app2::class, "set_profile", config::TEST]],
|
["-T", "--test", "action" => [app::class, "set_profile", config::TEST]],
|
||||||
["--devel", "action" => [app2::class, "set_profile", config::DEVEL]],
|
["--devel", "action" => [app::class, "set_profile", config::DEVEL]],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -364,7 +363,7 @@ EOT);
|
||||||
|
|
||||||
/** retourner le profil courant en couleur */
|
/** retourner le profil courant en couleur */
|
||||||
static function get_profile(?string $profile=null): string {
|
static function get_profile(?string $profile=null): string {
|
||||||
if ($profile === null) $profile = app2::get_profile();
|
if ($profile === null) $profile = app::get_profile();
|
||||||
foreach (static::PROFILE_COLORS as $text => $color) {
|
foreach (static::PROFILE_COLORS as $text => $color) {
|
||||||
if (strpos($profile, $text) !== false) {
|
if (strpos($profile, $text) !== false) {
|
||||||
return $color? "<color $color>$profile</color>": $profile;
|
return $color? "<color $color>$profile</color>": $profile;
|
Loading…
Reference in New Issue