repo reorg

This commit is contained in:
Tom Mortensen
2023-06-02 12:49:33 -07:00
parent 25e934f0a7
commit f9ec00b488
699 changed files with 6700 additions and 68456 deletions
@@ -0,0 +1,101 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2019-2023, Andrew Zawadzki.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
//add translations
$_SERVER['REQUEST_URI'] = "plugins";
require_once "$docroot/plugins/dynamix/include/Translations.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
require_once "$docroot/plugins/dynamix/include/Secure.php";
function download_url($url, $path = "") {
$ch = curl_init();
curl_setopt_array($ch,[
CURLOPT_URL => $url,
CURLOPT_FRESH_CONNECT => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 45,
CURLOPT_ENCODING => "",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true
]);
$out = curl_exec($ch);
curl_close($ch);
if ( $path ) file_put_contents($path,$out);
return $out ?: false;
}
switch ($_POST['action']) {
case 'checkPlugin':
$options = $_POST['options'] ?? '';
$plugin = $options['plugin'] ?? '';
$name = unbundle($options['name'] ?? $plugin);
$file = "/boot/config/plugins/$plugin";
$file = realpath($file)==$file ? $file : "";
if ( ! $plugin || ! file_exists($file) ) {
echo json_encode(["updateAvailable"=>false]);
break;
}
exec("mkdir -p /tmp/plugins");
@unlink("/tmp/plugins/$plugin");
$url = plugin("pluginURL","/boot/config/plugins/$plugin");
download_url($url,"/tmp/plugins/$plugin");
$changes = plugin("changes","/tmp/plugins/$plugin");
$alerts = plugin("alert","/tmp/plugins/$plugin");
$version = plugin("version","/tmp/plugins/$plugin");
$installedVersion = plugin("version","/boot/config/plugins/$plugin");
$min = plugin("min","/tmp/plugins/$plugin") ?: "6.4.0";
if ( $changes ) {
file_put_contents("/tmp/plugins/".pathinfo($plugin, PATHINFO_FILENAME).".txt",$changes);
} else {
@unlink("/tmp/plugins/".pathinfo($plugin, PATHINFO_FILENAME).".txt");
}
if ( $alerts ) {
file_put_contents('/tmp/plugins/my_alerts.txt',$alerts);
} else {
@unlink('/tmp/plugins/my_alerts.txt');
}
$update = false;
if ( strcmp($version,$installedVersion) > 0 ) {
$unraid = parse_ini_file("/etc/unraid-version");
$update = version_compare($min,$unraid['version'],'<=');
}
$updateMessage = sprintf(_("%s: An update is available."),$name);
$linkMessage = sprintf(_("Click here to install version %s"),$version);
echo json_encode(["updateAvailable"=>$update, "version"=>$version, "min"=>$min, "alert"=>$alerts, "changes"=>$changes, "installedVersion"=>$installedVersion, "updateMessage"=>$updateMessage, "linkMessage"=>$linkMessage]);
break;
case 'addRebootNotice':
$message = htmlspecialchars(trim($_POST['message']));
if ( ! $message ) break;
$existing = @file("/tmp/reboot_notifications",FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
$existing[] = $message;
file_put_contents("/tmp/reboot_notifications",implode("\n",array_unique($existing)));
break;
case 'removeRebootNotice':
$message = htmlspecialchars(trim($_POST['message']));
$existing = file_get_contents("/tmp/reboot_notifications");
$newReboots = str_replace($message,"",$existing);
file_put_contents("/tmp/reboot_notifications",$newReboots);
break;
}
?>
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
function write(...$messages){
global $nchan;
if ($nchan) {
$com = curl_init();
curl_setopt_array($com,[
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true
]);
foreach ($messages as $message) {
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
curl_exec($com);
}
curl_close($com);
} else {
foreach ($messages as $message) echo $message;
}
}
write(_("Checking connectivity")." ...\n");
if (exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -om1 'OK'")) {
$check = popen('plugin checkall','r');
while (!feof($check)) write(fgets($check));
pclose($check);
} else {
write(_("No response, aborting")."!\n");
}
if ($nchan) write('_DONE_','');
?>
+442
View File
@@ -0,0 +1,442 @@
#!/usr/bin/php -q
<?PHP
// Copyright 2005-2023, Lime Technology
// License: GPLv2 only
//
// Program updates made by Bergware International (April 2020)
// Program updates made by Bergware International (June 2022)
$usage = <<<EOF
Process language files.
Usage: language install LANGUAGE-FILE
install a language
Usage: language [attribute name] LANGUAGE-FILE
obtain an attribute value
Usage: language check LANGUAGE
check and output the latest version of the language
Usage: language update LANGUAGE
update the language
Usage: language remove LANGUAGE
remove the language
Usage: language checkall
check the latest version of all installed languages
Usage: language updateall
update all installed languages
EOF;
// Error code to description (wget)
// ref: https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html
//
function error_desc($code) {
switch($code) {
case 0: return 'No errors';
case -1: return 'Generic error';
case 1: return 'Generic error';
case 2: return 'Parse error';
case 3: return 'File I/O error';
case 4: return 'Network failure';
case 5: return 'SSL verification failure';
case 6: return 'Username/password authentication failure';
case 7: return 'Protocol errors';
case 8: return 'Invalid URL / Server error response';
default: return 'Error code '.$code;
}
}
// Signal DONE to caller
//
function done($code) {
global $nchan;
if ($nchan) write('_DONE_','');
exit($code);
}
// Function to write either to console (echo) or nchan (curl)
// Default output is console, use optional parameter "nchan" to write to nchan instead
//
function write(...$messages){
global $nchan;
if ($nchan) {
$com = curl_init();
curl_setopt_array($com,[
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true
]);
foreach ($messages as $message) {
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
curl_exec($com);
}
curl_close($com);
} else {
foreach ($messages as $message) echo $message;
}
}
// Run command and obtain output
//
function run($command) {
$run = popen($command,'r');
while (!feof($run)) write(fgets($run));
return pclose($run);
}
// Run hooked scripts before correct execution of "method"
// method = install, update, remove, check
// hook programs receives three parameters: type=language and method and language-name
//
function pre_hooks() {
global $method, $name;
$language = (pathinfo($name)['extension']??'')=='xml' ? $name : "lang-$name.xml";
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/pre-hooks";
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
write("Executing hook script: ".basename($hook)."\n");
run("$hook language $method $language");
}
}
// Run hooked scripts after successful or failed completion of "method"
// method = install, update, remove, check
// hook programs receives four parameters: type=language and method and language-name and error (empty if none)
//
function post_hooks($error='') {
global $method, $name;
$language = (pathinfo($name)['extension']??'')=='xml' ? $name : "lang-$name.xml";
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/post-hooks";
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
write("Executing hook script: ".basename($hook)."\n");
run("$hook language $method $language $error");
}
}
// Download a file from a URL.
// Returns TRUE if success else FALSE and fills in error.
//
function download($url, $name, &$error) {
$plg = basename($url);
if ($file = popen("wget --compression=auto --no-cache --progress=dot -O $name $url 2>&1", 'r')) {
write("language: downloading: $plg ...\r");
$level = -1;
while (!feof($file)) {
if (preg_match('/\d+%/', fgets($file), $matches)) {
$percentage = substr($matches[0],0,-1);
if ($percentage > $level) {
write("language: downloading: $plg ... $percentage%\r");
$level = $percentage;
}
}
}
if (($perror = pclose($file)) == 0) {
write("language: downloading: $plg ... done\n");
return true;
} else {
$error = "$plg download failure (".error_desc($perror).")";
return false;
}
} else {
$error = "$plg failed to open";
return false;
}
}
// Deal with logging message.
//
function logger($message) {
shell_exec("logger $message");
}
// Interpret a language file
// Returns TRUE if success, else FALSE and fills in error string.
//
function language($method, $xml_file, &$error) {
global $docroot, $boot, $plugins, $tmp;
// parse language XML file
$xml = file_exists($xml_file) ? @simplexml_load_file($xml_file,NULL,LIBXML_NOCDATA) : false;
if ($xml === false) {
$error = "XML file doesn't exist or xml parse error";
return false;
}
switch ($method) {
case 'install':
$url = $xml->LanguageURL;
$name = $xml->LanguagePack;
$save = "$boot/dynamix/lang-$name.zip";
if (!file_exists($save)) {
if ($url) {
if (!download($url, $save, $error)) {
@unlink($save);
return false;
}
} else {
$error = "missing URL";
return false;
}
}
$path = "$docroot/languages/$name";
exec("mkdir -p $path");
@unlink("$docroot/webGui/javascript/translate.$name.js");
foreach (glob("$path/*.dot",GLOB_NOSORT) as $dot_file) unlink($dot_file);
exec("unzip -qqLjo -d $path $save", $dummy, $err);
if ($err > 1) {
@unlink($save);
exec("rm -rf $path");
$error = "unzip failed. Error code $err";
return false;
}
return true;
case 'remove':
$name = $xml->LanguagePack;
if ($name) {
$path = "$docroot/languages/$name";
exec("rm -rf $path");
@unlink("$docroot/webGui/javascript/translate.$name.js");
@unlink("$boot/lang-$name.xml");
@unlink("$plugins/lang-$name.xml");
@unlink("$tmp/lang-$name.xml");
@unlink("$boot/dynamix/lang-$name.zip");
return true;
} else {
$error = "missing language pack";
return false;
}
case 'dump':
// dump file: debugging
write(print_r($xml,true));
return true;
default:
// return single attribute
$error = "$method attribute not present";
return $xml->$method ?: false;
}
}
$docroot = '/usr/local/emhttp';
$boot = '/boot/config/plugins';
$plugins = '/var/log/plugins';
$tmp = '/tmp/plugins';
$method = $argv[1];
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
// MAIN - single argument
if ($argc < 2) {
write($usage);
done(1);
}
// language checkall
// check all installed languages
//
if ($method == 'checkall') {
write("language: checking all language packs\n");
foreach (glob("$plugins/lang-*.xml", GLOB_NOSORT) as $link) {
$lang_file = @readlink($link);
if ($lang_file === false) continue;
if (language('LanguageURL', $lang_file, $error) === false) continue;
$name = str_replace('lang-', '', basename($lang_file, '.xml'));
$lang = language('Language', $lang_file, $error) ?: $name;
write("language: checking $lang language pack ...\n");
exec(realpath($argv[0])." check $name >/dev/null");
}
write("language: checking finished.\n");
done(0);
}
// language updateall
// update all installed languages
//
if ($method == 'updateall') {
write("language: updating all language packs\n");
foreach (glob("$plugins/lang-*.xml", GLOB_NOSORT) as $link) {
$lang_file = @readlink($link);
if ($lang_file === false) continue;
if (language('LanguageURL', $lang_file, $error) === false) continue;
$version = language('Version', $lang_file, $error);
$name = str_replace('lang-', '', basename($lang_file, '.xml'));
$lang = language('Language', $lang_file, $error) ?: $name;
$latest = language('Version', "$tmp/lang-$name.xml", $error);
// update only when newer
if (strcmp($latest, $version) > 0) {
write("language: updating $lang language pack ...\n");
exec(realpath($argv[0])." update $name >/dev/null");
}
}
write("language: updating finished.\n");
done(0);
}
// MAIN - two arguments
if ($argc < 3) {
write($usage);
done(1);
}
// language install [language_file]
//
if ($method == 'install') {
$argv[2] = preg_replace('#[\x00-\x1F\x80-\xFF]#', '', $argv[2]);
$name = basename($argv[2]);
write("language: installing language pack\n");
// check for URL
if (preg_match('#^https?://#',$argv[2])) {
$langURL = $argv[2];
$xml_file = "$tmp/$name";
write("language: downloading: $name\n");
if (!download($langURL, $xml_file, $error)) {
write("language: $error\n");
@unlink($xml_file);
done(1);
}
} else {
$xml_file = realpath($argv[2]);
}
$link_file = "$plugins/$name";
$lang_file = "$boot/$name";
@unlink($link_file);
// run hook scripts for pre processing
pre_hooks();
if (language('install', $xml_file, $error) === false) {
write("language: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
$lang = language('Language', $xml_file, $error) ?: substr($name,0,-4);
copy($xml_file, $lang_file);
symlink($lang_file, $link_file);
write("language: $lang language pack installed\n");
logger("language: $lang language pack installed");
// run hook scripts for post processing
post_hooks();
done(0);
}
// language check [language]
//
if ($method == 'check') {
$name = $argv[2];
write("language: checking language pack\n");
$link_file = "$plugins/lang-$name.xml";
$lang_file = @readlink($link_file);
if ($lang_file === false) {
write("language: $name language pack not installed\n");
done(1);
}
$templateURL = language('TemplateURL', $lang_file, $error);
if ($templateURL === false) {
write("language: $error\n");
done(1);
}
$xml_file = "$tmp/lang-$name.xml";
if (!download($templateURL, $xml_file, $error)) {
write("language: $error\n");
@unlink($xml_file);
done(1);
}
// run hook scripts for pre processing
pre_hooks();
$version = language('Version', $xml_file, $error);
if ($version === false) {
write("language: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
write("$version\n");
// run hook scripts for post processing
post_hooks();
done(0);
}
// language update [language]
//
if ($method == 'update') {
$name = $argv[2];
write("language: updating language pack\n");
$link_file = "$plugins/lang-$name.xml";
$lang_file = @readlink($link_file);
if ($lang_file === false) {
write("language: $name language pack not installed\n");
done(1);
}
// verify previous check has been done
$xml_file = "$tmp/lang-$name.xml";
if (!file_exists($xml_file)) {
write("language: update does not exist, perform a check first\n");
exit (1);
}
$lang = language('Language', $xml_file, $error) ?: $name;
// check for a reinstall of same version
$old_version = language('Version', $lang_file, $error);
$new_version = language('Version', $xml_file, $error);
if ($new_version == $old_version) {
write("language: $lang language pack not reinstalling same version\n");
done(1);
}
// install the updated plugin
@unlink("$boot/dynamix/lang-$name.zip");
@unlink($link_file);
// run hook scripts for pre processing
pre_hooks();
if (language('install', $xml_file, $error) === false) {
write("language: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
copy($xml_file, $lang_file);
symlink($lang_file, $link_file);
write("language: $lang language pack updated\n");
logger("language: $lang language pack updated");
// run hook scripts for post processing
post_hooks();
done(0);
}
// language remove [language]
//
if ($method == 'remove') {
$name = $argv[2];
write("language: removing language pack: $name\n");
$link_file = "$plugins/lang-$name.xml";
$lang_file = @readlink($link_file);
if ($lang_file === false) {
write("language: $name language pack not installed\n");
done(1);
}
$lang = language('Language', $lang_file, $error) ?: $name;
// run hook scripts for pre processing
pre_hooks();
if (language('remove', $lang_file, $error) === false) {
write("language: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
write("language: $lang language pack removed\n");
logger("language: $lang language pack removed");
// run hook scripts for post processing
post_hooks();
done(0);
}
// return attribute
//
$xml_file = $argv[2];
$value = language($method, $xml_file, $error);
if ($value === false) {
write("language: $error\n");
done(1);
}
write("$value\n");
done(0);
?>
@@ -0,0 +1,55 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
extract(parse_plugin_cfg('dynamix', true));
// Multi-language support
$_SERVER['REQUEST_URI'] = "scripts";
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$var = @parse_ini_file('/var/local/emhttp/var.ini') ?: [];
function apos($text) {
// So that "&apos;" doesn't show up in email
return str_replace("&apos;","'",$text);
}
$server = strtoupper($var['NAME']);
$output = _var($notify,'language_notify');
$script = "$docroot/webGui/scripts/notify";
$tmp = '/tmp/plugins';
$plugins = '/var/log/plugins';
language('checkall');
foreach (glob("/$tmp/lang-*.xml", GLOB_NOSORT) as $file) {
$name = basename($file);
$lang = language('LanguageLocal', $file);
$new = language('Version', $file);
$old = language('Version', "$plugins/$name");
// silently suppress bad download of XML file
if (strcmp($new, $old) > 0) {
$event = apos(_("Language")." - $lang [$new]");
$subject = apos(sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$description = apos(sprintf(_("A new version of %s is available"),$lang));
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Apps' -x");
}
}
exit(0);
?>
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$method = $argv[1]??'';
$plugins = explode('*',$argv[2]??'');
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
$call = ['plg' => 'plugin', 'xml' => 'language', '' => 'language'];
function write(...$messages){
global $nchan;
if ($nchan) {
$com = curl_init();
curl_setopt_array($com,[
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true
]);
foreach ($messages as $message) {
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
curl_exec($com);
}
curl_close($com);
} else {
foreach ($messages as $message) echo $message;
}
}
foreach ($plugins as $plugin) {
if (!$plugin || (!$cmd = $call[pathinfo($plugin,PATHINFO_EXTENSION)])) continue;
$line = '';
$pluginArg = $method == "update" ? basename($plugin) : $plugin;
$run = popen("$cmd $method $pluginArg",'r');
while (!feof($run)) {
$line .= fgetc($run);
if (in_array($line[-1],["\r","\n"])) {write($line); $line = '';}
}
pclose($run);
write("\n");
}
if ($nchan) write('_DONE_','');
?>
+887
View File
@@ -0,0 +1,887 @@
#!/usr/bin/php -q
<?PHP
// Copyright 2005-2022, Lime Technology
// License: GPLv2 only
//
// Program updates made by Bergware International (April 2020)
// Program updates made by Bergware International (June 2022)
$usage = <<<EOF
Process plugin files.
Usage: plugin install PLUGIN-FILE [forced]
install a plugin
PLUGIN-FILE is a plugin definition XML file with ".plg" extension.
PLUGIN-FILE can be a local file, or a URL. If a URL, the plugin file is first downloaded to /tmp/plugins.
forced is optional and can be used to install a lower version than currently running.
This command will process all FILE elements in PLUGIN-FILE which are tagged with the "install" method (or
that have no method tag).
This command has two major use cases:
1) Invoked at system startup by /etc/rc.d/rc.local on each .plg file found int /boot/config/plugins.
Upon success we register the plugin as "installed" by creating a symlink to it in /var/log/plugins.
If any kind of error, we move the file to /boot/config/plugins-error.
If a symlink already exists for the plugin file, this indicates a plugin replacing a "built-in" plugin. In
this case, if the version of PLUGIN-FILE is newer than the built-in plugin, we go ahead and install normally;
otherwise, we move to /boot/config/plugins-stale.
2) Invoked manually or via Plugin Manager for a .plg file not in /boot/config/plugins.
If a symlink already exists for the plugin file, this indicates a plugin update. In this case, if the version of
PLUGIN-FILE is newer than the built-in plugin, we go ahead and install normally and then move the old plugin
to /boot/config/plugins-stale.
Upon success we copy PLUGIN-FILE to /boot/config/plugins and register it as "installed" by creating a
symlink to it in /var/log/plugins.
Usage: plugin remove PLUGIN
remove a plugin
PLUGIN is the file basename of a plugin, e.g., "myplugin.plg".
If PLUGIN is found in /var/log/plugins then this command will process all FILE elements in PLUGIN which are
tagged with the "remove" method. Upon success we delete /var/log/plugins/PLUGIN and move the plugin
file to /boot/config/plugins-removed
Usage: plugin check PLUGIN
check and output the latest version of PLUGIN
We extract the pluginURL attribute from PLUGIN and use it to download (presumably the latest
version of) the plugin file to /tmp/plugins/ directory, and then output the version string.
Usage: plugin checkall
check all installed plugins
Runs 'plugin check PLUGIN' for each plugin file linked-to in /var/log/plugins.
Usage: plugin update PLUGIN
update the plugin
We look for the new plugin in /tmp/plugins/ directory. If found then we first execute the "install"
method of each FILE in the new plugin. (If necessary, a plugin can detect that this is an
"update" by checking for the existence of /var/log/plugins/PLUGIN.) If successful, we
delete the "old" plugin file from /boot/config/plugins/, copy the "new" plugin file from
/tmp/plugins/ to /boot/config/plugins/, and finally create the new symlink.
Note: to support `plugin check` and `plugin update` the plugin file must contain both "pluginURL" and
"version" attributes.
Usage: plugin [attribute name] PLUGIN-FILE
Any method which is not one of the actions listed above is assumed to be the name of an attribute of
the <PLUGIN> tag within PLUGIN-FILE. If the attribute exists, its value (a string) is output and the command
exit status is 0. If the attribute does not exist, command exit status is 1.
The plugin manager recognizes this set of attributes for the <PLUGIN> tag:
name - MANDATORY plugin name, e.g., "myplugin" or "my-plugin" etc.
This tag defines the name of the plugin. The name should omit embedded information such as architecture,
version, author, etc.
The plugin should create a directory under `/usr/local/emhttp/plugins` named after
the plugin, e.g., `/usr/local/emhttp/plugins/myplugin`. Any webGui pages, icons, README files, etc, should
be created inside this directory.
The plugin should also create a directory under `/boot/config/plugins` named after the plugin, e.g.,
`/boot/config/plugins/myplugin`. Here is where you store plugin-specific files such as a configuration
file and icon file. Note that this directory exists on the users USB Flash device and writes should be
minimized.
Upon successful installation, the plugin manager will copy the input plugin file to `/boot/config/plugins`
on the users USB Flash device, and create a symlink in `/var/log/plugins` also named after the plugin,
e.g., `/var/log/plugins/myplugin`. Each time the unRaid server is re-booted, all plugins stored
in `/boot/config/plugins` are automatically installed; plugin authors should be aware of this behavior.
author - OPTIONAL
Whatever you put here will show up under the **Author** column in the Plugin List. If this attribute
is omitted we display "anonymous".
version - MANDATORY
Use a string suitable for comparison to determine if one version is older/same/newer than another version.
Any format is acceptable but LimeTech uses "YYYY.MM.DD", e.g., "2014.02.18" (if multiple versions happen
to get posted on the same day we add a letter suffix, e.g., "2014.02.18a").
pluginURL - OPTIONAL but MANDATORY if you want "check for updates" to work with your plugin
This is the URL of the plugin file to download and extract the **version** attribute from to determine if
this is a new version.
More attributes may be defined in the future.
Here is the set of directories and files used by the plugin system:
/boot/config/plugins/
This directory contains the plugin files for plugins to be (re)installed at boot-time. Upon
successful `plugin install`, the plugin file is copied here (if not here already). Upon successful
`plugin remove`, the plugin file is deleted from here.
/boot/config/plugins-error/
This directory contains plugin files that failed to install.
/boot/config/plugins-removed/
This directory contains plugin files that have been removed.
/boot/config/plugins-stale/
This directory contains plugin files that failed to install because a newer version of the same plugin is
already installed.
/tmp/plugins/
This directory is used as a target for downloaded plugin files. The `plugin check` operation
downloads the plugin file here and the `plugin update` operation looks for the plugin to update here.
/var/log/plugins/
This directory contains a symlink named after the plugin name (not the plugin file name) which points to
the actual plugin file used to install the plugin. The existence of this file indicates successful
install of the plugin.
EOF;
// Error code to description (wget)
// ref: https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html
//
function error_desc($code) {
switch($code) {
case 0: return 'No errors';
case -1: return 'Generic error';
case 1: return 'Generic error';
case 2: return 'Parse error';
case 3: return 'File I/O error';
case 4: return 'Network failure';
case 5: return 'SSL verification failure';
case 6: return 'Username/password authentication failure';
case 7: return 'Protocol errors';
case 8: return 'Invalid URL / Server error response';
default: return 'Error code '.$code;
}
}
// Signal DONE to caller
//
function done($code) {
global $nchan;
if ($nchan) write('_DONE_','');
exit($code);
}
// Function to write either to console (echo) or nchan (curl)
// Default output is console, use optional parameter "nchan" to write to nchan instead
//
function write(...$messages){
global $nchan;
if ($nchan) {
$com = curl_init();
curl_setopt_array($com,[
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true
]);
foreach ($messages as $message) {
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
curl_exec($com);
}
curl_close($com);
} else {
foreach ($messages as $message) echo $message;
}
}
// Run command and obtain output
//
function run($command) {
$run = popen($command,'r');
while (!feof($run)) write(fgets($run));
return pclose($run);
}
// Run hooked scripts before correct execution of "method"
// method = install, update, remove, check
// hook programs receives three parameters: type=plugin and method and plugin-name
//
function pre_hooks() {
global $method, $plugin;
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/pre-hooks";
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
write("Executing hook script: ".basename($hook)."\n");
run("$hook plugin $method $plugin");
}
}
// Run hooked scripts after successful or failed completion of "method"
// method = install, update, remove, check
// hook programs receives four parameters: type=plugin and method and plugin-name and error (empty if none)
//
function post_hooks($error='') {
global $method, $plugin;
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/post-hooks";
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
write("Executing hook script: ".basename($hook)."\n");
run("$hook plugin $method $plugin $error");
}
}
// Download a file from a URL.
// Returns TRUE if success else FALSE and fills in error.
//
function download($url, $name, &$error, $write=true) {
if ($url) {
$plg = basename($url);
$plg = str_replace('"', '', $plg);
$tries = (strpos($url, '.cdn') !== false) ? "1" : "3";
if ($file = popen("wget --compression=auto --no-cache --progress=dot --retry-connrefused --prefer-family=IPv4 --timeout=10 --tries=$tries --waitretry=$tries -O $name $url 2>&1", 'r')) {
if ($write) write("plugin: downloading: $plg ...\r");
$level = -1;
while (($line = fgets($file)) !== false) {
if (preg_match('/ \d+% /', $line, $matches)) {
$percentage = substr(trim($matches[0]),0,-1);
if ($percentage > $level) {
if ($write) write("plugin: downloading: $plg ... $percentage%\r");
$level = $percentage;
}
}
}
if (($perror = pclose($file)) != 0) {
$error = "$plg download failure: ".error_desc($perror);
return false;
} elseif (filesize($name) == 0) {
if ($write) write("plugin: download failure: zero-length file\r","\n");
return false;
} else {
if ($write) write("plugin: downloading: $plg ... done\r","\n");
return true;
}
} else {
$error = "$plg failed to open";
return false;
}
} else {
// The url is empty
return false;
}
}
// Filter URL.
// If URL contains '.cdn' remove the '.cdn' from the URL.
// Returns URL without '.cdn' if found.
//
function filter_url($url) {
if (strpos($url, '.cdn') !== false) {
$new_url = str_replace('"', '', $url);
$new_url = str_replace('.cdn', '', $new_url);
} else {
$new_url = "";
}
return($new_url);
}
// Deal with logging message.
//
function logger($message) {
shell_exec("logger $message");
}
// Interpret a plugin file
// Returns TRUE if success, else FALSE and fills in error string.
//
// If a FILE element does not have a Method attribute, we treat as though Method is "install".
// A FILE Method attribute can list multiple methods separated by spaces in which case that file
// is processed for any of those methods.
//
function plugin($method, $plugin_file, &$error) {
global $unraid;
$methods = ['install', 'remove'];
// parse plugin definition XML file
$xml = file_exists($plugin_file) ? @simplexml_load_file($plugin_file, NULL, LIBXML_NOCDATA) : false;
if ($xml === false) {
$error = "XML file doesn't exist or xml parse error";
return false;
}
// dump
if ($method == 'dump') {
// dump file: debugging
write(print_r($xml,true));
return true;
}
// release notes
if ($method == 'changes') {
return $xml->CHANGES ? trim($xml->CHANGES) : false;
}
// alert message
if ($method == 'alert') {
return $xml->ALERT ? trim($xml->ALERT) : false;
}
// validate plugin download without installation
if ($method == 'validate') {
$name = '/tmp/validate-plugin.tmp';
foreach ($xml->FILE as $file) if ($file->URL) {
if (!$file->SHA256 and !$file->MD5) continue;
if ( (download($file->URL, $name, $error, false) === false) && (download(filter_url($file->URL), $name, $error, false) === false) ) {
@unlink($name);
return false;
}
if (($file->SHA256 && hash_file('sha256',$name) != $file->SHA256) or ($file->MD5 && md5_file($name) != $file->MD5)) {
$error = "bad hash value";
@unlink($name);
return false;
}
}
@unlink($name);
return "valid";
}
// check if $method is an attribute
if (!in_array($method, $methods)) {
foreach ($xml->attributes() as $key => $value) {
if ($method == $key) return $value;
}
$error = "$method attribute not present";
return false;
}
// Process FILE elements in order
//
foreach ($xml->FILE as $file) {
// skip if not our $method
if (isset($file->attributes()->Method)) {
if (!in_array($method, explode(" ", $file->attributes()->Method))) continue;
} elseif ($method != 'install') continue;
$name = $file->attributes()->Name;
// bergware - check Unraid version dependency (if present)
$min = $file->attributes()->Min;
if ($min && version_compare($unraid['version'],$min,'<')) {
write("plugin: skipping: ".basename($name)." - Unraid version too low, requires at least version $min\n");
continue;
}
$max = $file->attributes()->Max;
if ($max && version_compare($unraid['version'],$max,'>')) {
write("plugin: skipping: ".basename($name)." - Unraid version too high, requires at most version $max\n");
continue;
}
// Name can be missing but only makes sense if Run attribute is present
if ($name) {
// Ensure parent directory exists
//
if (!file_exists(dirname($name))) {
if (!mkdir(dirname($name), 0770, true)) {
$error = "unable to create parent directory for $name";
return false;
}
}
// If file already exists, check the SHA256/MD5 (if supplied)
if (file_exists($name)) {
if ($file->SHA256) {
logger("plugin: checking: $name - SHA256");
if (hash_file('sha256', $name) != $file->SHA256) {
unlink($name);
}
} elseif ($file->MD5) {
logger("plugin: checking: $name - MD5");
if (md5_file($name) != $file->MD5) {
unlink($name);
}
}
}
// If file already exists, do not overwrite
//
if (file_exists($name)) {
logger("plugin: skipping: $name already exists");
} elseif ($file->LOCAL) {
// Create the file
//
// for local file, just copy it
logger("plugin: creating: $name - copying LOCAL file $file->LOCAL");
if (!copy($file->LOCAL, $name)) {
$error = "unable to copy LOCAL file: $name";
@unlink($name);
return false;
}
} elseif ($file->INLINE) {
// for inline file, create with inline contents
logger("plugin: creating: $name - from INLINE content");
$contents = trim($file->INLINE).PHP_EOL;
if ($file->attributes()->Type == 'base64') {
logger("plugin: decoding: $name as base64");
$contents = base64_decode($contents);
if ($contents === false) {
$error = "unable to decode inline base64: $name";
return false;
}
}
if (!file_put_contents($name, $contents)) {
$error = "unable to create file: $name";
@unlink($name);
return false;
}
} elseif ($file->URL) {
// for download file, download and maybe verify the file MD5
logger("plugin: creating: $name - downloading from URL $file->URL");
if ( (download($file->URL, $name, $error) === false) && (download(filter_url($file->URL), $name, $error) === false) ) {
@unlink($name);
return false;
}
if ($file->SHA256) {
logger("plugin: checking: $name - SHA256");
if (hash_file('sha256', $name) != $file->SHA256) {
$error = "bad file SHA256: $name";
unlink($name);
return false;
}
} elseif ($file->MD5) {
logger("plugin: checking: $name - MD5");
if (md5_file($name) != $file->MD5) {
$error = "bad file MD5: $name";
unlink($name);
return false;
}
}
}
// Maybe change the file mode
//
if ($file->attributes()->Mode) {
// if file has 'Mode' attribute, apply it
$mode = $file->attributes()->Mode;
logger("plugin: setting: $name - mode to $mode");
if (!chmod($name, octdec($mode))) {
$error = "chmod failure: $name";
return false;
}
}
}
// Maybe "run" the file now
//
if ($file->attributes()->Run) {
$command = $file->attributes()->Run;
if ($name) {
logger(escapeshellarg("plugin: running: $command $name"));
$retval = run("$command $name");
} elseif ($file->LOCAL) {
logger(escapeshellarg("plugin: running: $command $file->LOCAL"));
$retval = run("$command $file->LOCAL");
} elseif ($file->INLINE) {
logger("plugin: running: 'anonymous'");
$name = '/tmp/inline.sh';
file_put_contents($name, $file->INLINE);
$retval = run("$command $name");
unlink($name);
}
if ($retval != 0) {
$error = "run failed: '$command' returned $retval";
return false;
}
}
}
return true;
}
function move($src_file, $tar_dir) {
@mkdir($tar_dir);
return rename($src_file, $tar_dir."/".basename($src_file));
}
$notify = '/usr/local/emhttp/webGui/scripts/notify';
$boot = '/boot/config/plugins';
$plugins = '/var/log/plugins';
$tmp = '/tmp/plugins';
$method = $argv[1];
$builtin = ['unRAIDServer','unRAIDServer-'];
$nchan = $argv[$argc-1] == 'nchan'; // console or nchan output
// In following code,
// $plugin - is a basename of a plugin, eg, "myplugin.plg"
// $plugin_file - is an absolute path, eg, "/boot/config/plugins/myplugin.plg"
//
// MAIN - single argument
if ($argc < 2) {
write($usage);
done(1);
}
// plugin checkall
// check all installed plugins, except built-in
//
if ($method == 'checkall') {
if (!$cmd = realpath($argv[0])) {
write("Unknown command: {$argv[0]}\n");
done(1);
}
foreach (glob("$plugins/*.plg", GLOB_NOSORT) as $link) {
// skip OS related plugins
if (in_array(basename($link,'.plg'),$builtin)) continue;
// only consider symlinks
$installed_plugin_file = @readlink($link);
if ($installed_plugin_file === false) continue;
if (plugin('pluginURL', $installed_plugin_file, $error) === false) continue;
$plugin = basename($installed_plugin_file);
write("plugin: checking $plugin ...\n");
exec("$cmd check $plugin >/dev/null");
}
write("plugin: checking finished.\n");
done(0);
}
// plugin updateall
// update all installed plugins, which have a update available
//
if ($method == 'updateall') {
if (!$cmd = realpath($argv[0])) {
write("Unknown command: {$argv[0]}\n");
done(1);
}
foreach (glob("$plugins/*.plg", GLOB_NOSORT) as $link) {
// skip OS related plugins
if (in_array(basename($link,'.plg'),$builtin)) continue;
// only consider symlinks
$installed_plugin_file = @readlink($link);
if ($installed_plugin_file === false) continue;
if (plugin('pluginURL', $installed_plugin_file, $error) === false) continue;
$version = plugin('version', $installed_plugin_file, $error);
$plugin = basename($installed_plugin_file);
$latest = plugin('version', "$tmp/$plugin", $error);
// update only when newer
if (strcmp($latest,$version) > 0) {
write("plugin: updating $plugin ...\n");
exec("$cmd update $plugin >/dev/null");
}
}
write("plugin: updating finished.\n");
done(0);
}
// plugin checkos
// check built-in only
//
if ($method == 'checkos') {
if (!$cmd = realpath($argv[0])) {
write("Unknown command: {$argv[0]}\n");
done(1);
}
foreach ($builtin as $link) {
// only consider symlinks
$installed_plugin_file = @readlink("$plugins/$link.plg");
if ($installed_plugin_file === false) continue;
if (plugin("pluginURL", $installed_plugin_file, $error) === false) continue;
$plugin = basename($installed_plugin_file);
write("plugin: checking $plugin ...\n");
exec("$cmd check $plugin >/dev/null");
}
write("plugin: checking finished.\n");
done(0);
}
// MAIN - two or three arguments
if ($argc < 3) {
write($usage);
done(1);
}
// plugin install [plugin_file]
// cases:
// a) dirname of [plugin_file] is /boot/config/plugins (system startup)
// b) [plugin_file] is a URL
// c) dirname of [plugin_file] is not /boot/config/plugins
//
$unraid = parse_ini_file('/etc/unraid-version');
if ($method == 'install') {
$argv[2] = preg_replace('#[\x00-\x1F\x80-\xFF]#', '', $argv[2]);
$plugin = basename($argv[2]);
if (pathinfo($plugin, PATHINFO_EXTENSION) != "plg") {
write("plugin: $plugin is not a plg file\n");
done(1);
}
write("plugin: installing: $plugin\n");
// check for URL
if (preg_match('#^https?://#',$argv[2])) {
$pluginURL = $argv[2];
// run hook scripts for pre processing
pre_hooks();
$plugin_file = "$tmp/$plugin";
write("plugin: downloading: $plugin\n");
if ( (download($pluginURL, $plugin_file, $error) === false) && (download(filter_url($pluginURL), $plugin_file, $error) === false) ) {
write("plugin: $error\n");
@unlink($plugin_file);
// run hook scripts for post processing
post_hooks($error);
done(1);
}
} else {
$plugin_file = realpath($argv[2]);
}
// bergware - check Unraid version dependency (if present)
$min = plugin('min', $plugin_file, $error);
if ($min && version_compare($unraid['version'], $min, '<')) {
write("plugin: installed Unraid version is too low, require at least version $min\n");
if (dirname($plugin_file) == "$boot") {
move($plugin_file, "$boot-error");
}
// run hook scripts for post processing
post_hooks($error);
done(1);
}
$max = plugin('max', $plugin_file, $error) ?: plugin('Unraid', $plugin_file, $error);
if ($max && version_compare($unraid['version'], $max, '>')) {
write("plugin: installed Unraid version is too high, require at most version $max\n");
if (dirname($plugin_file) == "$boot") {
move($plugin_file, "$boot-error");
}
// run hook scripts for post processing
post_hooks($error);
done(1);
}
$symlink = "$plugins/$plugin";
// check for re-install
$installed_plugin_file = @readlink($symlink);
if ($installed_plugin_file !== false) {
if ($plugin_file == $installed_plugin_file) {
write("plugin: not re-installing same plugin\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// must have version attributes for re-install
$version = plugin('version', $plugin_file, $error);
if ($version === false) {
write("plugin: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
$installed_version = plugin('version', $installed_plugin_file, $error);
if ($installed_version === false) {
write("plugin: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// check version installation?
$forced = $nchan ? ($argc==5 ? $argv[4] : false) : ($argc==4 ? $argv[3] : false);
if (!$forced) {
// do not re-install if same plugin already installed or has higher version
if (strcmp($version, $installed_version) < 0) {
write("plugin: not installing older version\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
if (strcmp($version, $installed_version) === 0) {
write("plugin: not reinstalling same version\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
}
// run hook scripts for pre processing
pre_hooks();
if (plugin('install', $plugin_file, $error) === false) {
write("plugin: $error\n");
if (dirname($plugin_file) == "$boot") {
move($plugin_file, "$boot-error");
}
$event = "Install error";
$subject = "plugin: ".basename($plugin_file);
$description = "Plugin failed to install";
exec("$notify -e $event -s $subject -d $description) -i 2");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// remove symlink for re-install
unlink($symlink);
} else {
// run hook scripts for pre processing
pre_hooks();
// fresh install
if (plugin('install', $plugin_file, $error) === false) {
write("plugin: $error\n");
if (dirname($plugin_file) == "$boot") {
move($plugin_file, "$boot-error");
}
// run hook scripts for post processing
post_hooks($error);
done(1);
}
}
// register successful install
$target = "$boot/$plugin";
if (!plugin('noInstall', $plugin_file, $error)) {
if ($target != $plugin_file) copy($plugin_file, $target);
symlink($target, $symlink);
write("plugin: $plugin installed\n");
logger("plugin: $plugin installed");
} else {
write("script: $plugin executed\n");
logger("script: $plugin executed");
}
// run hook scripts for post processing
post_hooks();
done(0);
}
// plugin check [plugin]
// We use the pluginURL attribute to download the latest plg file into the "/tmp/plugins/"
// directory.
//
if ($method == 'check') {
$plugin = $argv[2];
$symlink = "$plugins/$plugin";
write("plugin: checking: $plugin ...\n");
$installed_plugin_file = @readlink($symlink);
if ($installed_plugin_file === false) {
write("plugin: not installed\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
$installed_pluginURL = plugin('pluginURL', $installed_plugin_file, $error);
if ($installed_pluginURL === false) {
write("plugin: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
$plugin_file = "$tmp/$plugin";
if ( (download($installed_pluginURL, $plugin_file, $error) === false) && (download(filter_url($installed_pluginURL), $plugin_file, $error) === false) ) {
write("plugin: $error\n");
@unlink($plugin_file);
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// run hook scripts for pre processing
pre_hooks();
$version = plugin('version', $plugin_file, $error);
if ($version === false) {
write("plugin: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
write("$version\n");
// run hook scripts for post processing
post_hooks();
done(0);
}
// plugin update [plugin]
// [plugin] is the plg file we are going to be replacing, eg, "old.plg".
// We assume a "check" has already been done, ie, "/tmp/plugins/new.plg" already exists.
// We execute the "install" method of new.plg. If this fails, then we mark old.plg "not installed";
// the plugin manager will recognize this as an install error.
// If install new.plg succeeds, then we remove old.plg and copy new.plg in place.
// Finally we mark the new.plg "installed".
//
if ($method == 'update') {
$plugin = $argv[2];
$symlink = "$plugins/$plugin";
write("plugin: updating: $plugin\n");
$installed_plugin_file = @readlink($symlink);
if ($installed_plugin_file === false) {
write("plugin: $plugin not installed\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// verify previous check has been done
$plugin_file = "$tmp/$plugin";
if (!file_exists($plugin_file)) {
write("plugin: $plugin_file does not exist, check for updates first\n");
exit (1);
}
// bergware - check Unraid version dependency (if present)
$min = plugin('min', $plugin_file, $error);
if ($min && version_compare($unraid['version'], $min, '<')) {
write("plugin: installed Unraid version is too low, require at least version $min\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
$max = plugin('max', $plugin_file, $error) ?: plugin('Unraid', $plugin_file, $error);
if ($max && version_compare($unraid['version'], $max, '>')) {
write("plugin: installed Unraid version is too high, require at most version $max\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// check for a reinstall of same version
if (strcmp(plugin('version', $installed_plugin_file, $error), plugin('version', $plugin_file, $error)) === 0) {
write("Not reinstalling same version\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// run hook scripts for pre processing
pre_hooks();
// install the updated plugin
if (plugin('install', $plugin_file, $error) === false) {
write("plugin: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
// install was successful, save the updated plugin so it installs again next boot
unlink($symlink);
$target = "$boot/$plugin";
copy($plugin_file, $target);
symlink($target, $symlink);
write("plugin: $plugin updated\n");
logger("plugin: $plugin updated");
// run hook scripts for post processing
post_hooks();
done(0);
}
// plugin remove [plugin]
// only .plg files should have a remove method
//
if ($method == 'remove') {
$plugin = $argv[2];
$symlink = "$plugins/$plugin";
write("plugin: removing: $plugin\n");
$installed_plugin_file = @readlink($symlink);
if ($installed_plugin_file !== false) {
// remove the symlink
unlink($symlink);
@unlink("$tmp/$plugin");
// run hook scripts for pre processing
pre_hooks();
if (plugin('remove', $installed_plugin_file, $error) === false) {
// but if can't remove, restore the symlink
if (is_file($installed_plugin_file)) symlink($installed_plugin_file, $symlink);
write("plugin: $error\n");
// run hook scripts for post processing
post_hooks($error);
done(1);
}
}
// remove the plugin file
move($installed_plugin_file, "$boot-removed");
write("plugin: $plugin removed\n");
logger("plugin: $plugin removed");
exec("/usr/local/sbin/update_cron");
// run hook scripts for post processing
post_hooks();
done(0);
}
// return attribute
//
$plugin_file = $argv[2];
$value = plugin($method, $plugin_file, $error);
if ($value === false) {
write("plugin: $error\n");
done(1);
}
write("$value\n");
done(0);
?>
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
write(){
for message in "$@"; do
curl -sfd "$message" --unix-socket /var/run/nginx.socket http://localhost/pub/plugins?buffer_length=1 >/dev/null 2>&1
done
}
# put some restrictions on 'delete'
if [[ $1 == /boot/config/plugins-error/* || $1 == /boot/config/plugins-stale/* ]]; then
text="Deleting $1 ..."
if [[ -z $2 ]]; then
echo "$text"
else
write "$text " "_DONE_" ""
fi
rm $1
fi
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
extract(parse_plugin_cfg('dynamix',true));
// Multi-language support
$_SERVER['REQUEST_URI'] = "scripts";
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$var = @parse_ini_file('/var/local/emhttp/var.ini') ?: [];
function apos($text) {
// So that "&apos;" doesn't show up in email
return str_replace("&apos;","'",$text);
}
$current = parse_ini_file('/etc/unraid-version');
$server = strtoupper(_var($var,'NAME','tower'));
$output = _var($notify,'plugin');
$builtin = ['unRAIDServer','unRAIDServer-'];
$script = "$docroot/webGui/scripts/notify";
$tmp = '/tmp/plugins';
$plugins = '/var/log/plugins';
plugin('checkall');
foreach (glob("/$tmp/*.plg", GLOB_NOSORT) as $file) {
$name = basename($file,'.plg');
// skip OS related updates
if (in_array($name,$builtin)) continue;
$new = plugin('version', $file);
$old = plugin('version', "$plugins/$name.plg");
$min = plugin('min', $file) ?: $current['version'];
// silently suppress bad download of PLG file
if (strcmp($new, $old)>0 && !version_compare($min,$current['version'],">")) {
$event = apos(_("Plugin")." - $name [$new]");
$subject = apos(sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$description = apos(sprintf(_("A new version of %s is available"),$name));
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Plugins' -x");
}
}
exit(0);
?>
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = 'plugins';
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$file = realpath($argv[1]??'');
$valid = ['/var/tmp/','/tmp/plugins/'];
$good = false;
foreach ($valid as $check) if (strncmp($file,$check,strlen($check))===0) $good = true;
if ($file && $good && pathinfo($file)['extension']=='txt') {
echo Markdown(file_get_contents($file));
} else {
echo Markdown("*"._('No release notes available')."!*");
}
?>
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
// Multi-language support
if (!function_exists('_')) {
function _($text) {return $text;}
}
extract(parse_plugin_cfg('dynamix', true));
$var = @parse_ini_file('/var/local/emhttp/var.ini') ?: [];
$script = "$docroot/webGui/scripts/notify";
$server = strtoupper(_var($var,'NAME','server'));
$output = _var($notify,'plugin');
$builtin = ['unRAIDServer'];
foreach ($builtin as $name) {
$plg = "$name.plg";
plugin('check',$plg);
$file = "/tmp/plugins/$plg";
$old = plugin('version', "/var/log/plugins/$plg");
$new = plugin('version', $file);
// silently suppress bad download of PLG file
if (version_compare($new,$old,'>')) {
exec("$script -e ".escapeshellarg("System - $name [$new]")." -s ".escapeshellarg("Notice [$server] - Version update $new")." -d ".escapeshellarg("A new version of $name is available")." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
}
}
exit(0);
?>