summaryrefslogtreecommitdiffstats
path: root/modules-available/minilinux/inc/minilinux.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/minilinux/inc/minilinux.inc.php')
-rw-r--r--modules-available/minilinux/inc/minilinux.inc.php186
1 files changed, 108 insertions, 78 deletions
diff --git a/modules-available/minilinux/inc/minilinux.inc.php b/modules-available/minilinux/inc/minilinux.inc.php
index d172d982..cbc797f2 100644
--- a/modules-available/minilinux/inc/minilinux.inc.php
+++ b/modules-available/minilinux/inc/minilinux.inc.php
@@ -11,21 +11,27 @@ class MiniLinux
const INVALID = 'invalid';
+ const INSTALL_MISSING = 0;
+
+ const INSTALL_OK = 1;
+
+ const INSTALL_BROKEN = 2;
+
/*
* Update of available versions by querying sources
*/
/**
- * Query all known sources for meta data
+ * Query all known sources for metadata
* @return int number of sources query was just initialized for
*/
- public static function updateList()
+ public static function updateList(): int
{
$stamp = time();
$last = Property::get(self::PROPERTY_KEY_FETCHTIME);
- if ($last !== false && $last + 10 > $stamp)
+ if ($last !== false && $last + 3 > $stamp)
return 0; // In progress...
- Property::set(self::PROPERTY_KEY_FETCHTIME, $stamp, 1);
+ Property::set(self::PROPERTY_KEY_FETCHTIME, $stamp, 10);
Database::exec('LOCK TABLES callback WRITE,
minilinux_source WRITE, minilinux_branch WRITE, minilinux_version WRITE');
Database::exec('UPDATE minilinux_source SET taskid = UUID()');
@@ -33,7 +39,8 @@ class MiniLinux
Database::exec("UPDATE minilinux_version
INNER JOIN minilinux_branch USING (branchid)
INNER JOIN minilinux_source USING (sourceid)
- SET orphan = orphan + 1 WHERE minilinux_source.lastupdate < $cutoff");
+ SET orphan = orphan + 1
+ WHERE minilinux_source.lastupdate < $cutoff AND orphan < 100");
$list = Database::queryAll('SELECT sourceid, url, taskid FROM minilinux_source');
foreach ($list as $source) {
Taskmanager::submit('DownloadText', array(
@@ -48,20 +55,22 @@ class MiniLinux
/**
* Called when downloading metadata from a specific update source is finished
- * @param mixed $task task structure
+ *
+ * @param array $task task structure
* @param string $sourceid see minilinux_source table
*/
- public static function listDownloadCallback($task, $sourceid)
+ public static function listDownloadCallback(array $task, string $sourceid): void
{
- if ($task['statusCode'] !== 'TASK_FINISHED')
+ if (!Taskmanager::isFinished($task))
return;
$taskId = $task['id'];
- $data = json_decode($task['data']['content'], true);
- if (!is_array($data)) {
- EventLog::warning('Cannot download Linux version meta data for ' . $sourceid);
+ $data = json_decode($task['data']['content'] ?? '', true);
+ if (!is_array($data) || empty($data['systems'])) {
+ EventLog::warning('Cannot download Linux version meta data for ' . $sourceid,
+ ($task['data']['error'] ?? '') . "\n\nContent:\n" . ($task['data']['content'] ?? ''));
$lastupdate = 'lastupdate';
} else {
- if (@is_array($data['systems'])) {
+ if (is_array($data['systems'])) {
self::addBranches($sourceid, $data['systems']);
}
$lastupdate = 'UNIX_TIMESTAMP()';
@@ -70,7 +79,8 @@ class MiniLinux
WHERE sourceid = :sourceid AND taskid = :taskid",
['sourceid' => $sourceid, 'taskid' => $taskId]);
// Clean up -- delete orphaned versions that are not installed
- Database::exec('DELETE FROM minilinux_version WHERE orphan > 4 AND installed = 0');
+ Database::exec('DELETE FROM minilinux_version WHERE orphan > 4 AND installed = :missing',
+ ['missing' => self::INSTALL_MISSING]);
// FKC makes sure we only delete orphaned ones
Database::exec('DELETE IGNORE FROM minilinux_branch WHERE 1', [], true);
}
@@ -81,18 +91,31 @@ class MiniLinux
if (!self::isValidIdPart($system['id']))
continue;
$branchid = $sourceid . '/' . $system['id'];
- $title = empty($system['title']) ? $branchid : $system['title'];
- $description = empty($system['description']) ? '' : $system['description'];
- Database::exec('INSERT INTO minilinux_branch (branchid, sourceid, title, description)
- VALUES (:branchid, :sourceid, :title, :description)
- ON DUPLICATE KEY UPDATE title = VALUES(title), description = VALUES(description)', [
- 'branchid' => $branchid,
- 'sourceid' => $sourceid,
- 'title' => $title,
- 'description' => $description,
- ]);
- if (@is_array($system['versions'])) {
+ $title = mb_substr(empty($system['title']) ? $branchid : $system['title'], 0, 150);
+ $description = $system['description'] ?? '';
+ $color = $system['color'] ?? '';
+ if (!empty($system['versions']) && is_array($system['versions'])) {
+ Database::exec('INSERT INTO minilinux_branch (branchid, sourceid, title, color, description)
+ VALUES (:branchid, :sourceid, :title, :color, :description)
+ ON DUPLICATE KEY UPDATE title = VALUES(title), color = VALUES(color), description = VALUES(description)', [
+ 'branchid' => $branchid,
+ 'sourceid' => $sourceid,
+ 'title' => $title,
+ 'color' => $color,
+ 'description' => $description,
+ ]);
self::addVersions($branchid, $system['versions']);
+ } else {
+ // Empty branch - only update metadata if branch exists locally
+ Database::exec('UPDATE minilinux_branch
+ SET title = :title, color = :color, description = :description
+ WHERE sourceid = :sourceid AND branchid = :branchid', [
+ 'branchid' => $branchid,
+ 'sourceid' => $sourceid,
+ 'title' => $title,
+ 'color' => $color,
+ 'description' => $description,
+ ]);
}
}
}
@@ -115,7 +138,8 @@ class MiniLinux
return;
}
$versionid = $branchid . '/' . $version['version'];
- $title = empty($version['title']) ? '' : $version['title'];
+ $title = $version['title'] ?? '';
+ $description = $version['description'] ?? '';
$dateline = empty($version['releasedate']) ? time() : (int)$version['releasedate'];
unset($version['version'], $version['title'], $version['releasedate']);
// Sanitize files array
@@ -148,18 +172,20 @@ class MiniLinux
$version['files'] = array_values($version['files']);
}
$data = json_encode($version);
- Database::exec('INSERT INTO minilinux_version (versionid, branchid, title, dateline, data, orphan)
- VALUES (:versionid, :branchid, :title, :dateline, :data, 0)
- ON DUPLICATE KEY UPDATE title = VALUES(title), data = VALUES(data), orphan = 0', [
+ Database::exec('INSERT INTO minilinux_version (versionid, branchid, title, description, dateline, data, orphan)
+ VALUES (:versionid, :branchid, :title, :description, :dateline, :data, 0)
+ ON DUPLICATE KEY UPDATE title = VALUES(title), description = VALUES(description),
+ dateline = VALUES(dateline), data = VALUES(data), orphan = 0', [
'versionid' => $versionid,
'branchid' => $branchid,
- 'title' => $title,
+ 'title' => mb_substr($title, 0, 150),
+ 'description' => $description,
'dateline' => $dateline,
'data' => $data,
]);
}
- private static function isValidIdPart($str)
+ private static function isValidIdPart(string $str): bool
{
return preg_match('/^[a-z0-9_\-]+$/', $str) > 0;
}
@@ -168,10 +194,10 @@ class MiniLinux
* Download of specific version
*/
- public static function validateDownloadTask($versionid, $taskid)
+ public static function validateDownloadTask(string $versionid, ?string $taskid): ?string
{
if ($taskid === null)
- return false;
+ return null;
$task = Taskmanager::status($taskid);
if (Taskmanager::isTask($task) && !Taskmanager::isFailed($task)
&& (is_dir(CONFIG_HTTP_DIR . '/' . $versionid) || !Taskmanager::isFinished($task)))
@@ -179,15 +205,13 @@ class MiniLinux
Database::exec('UPDATE minilinux_version SET taskid = NULL
WHERE versionid = :versionid AND taskid = :taskid',
['versionid' => $versionid, 'taskid' => $taskid]);
- return false;
+ return null;
}
/**
* Download the files for the given version id
- * @param $versionid
- * @return bool
*/
- public static function downloadVersion($versionid)
+ public static function downloadVersion(string $versionid): ?string
{
$ver = Database::queryFirst('SELECT s.url, s.pubkey, v.versionid, v.taskid, v.data FROM minilinux_version v
INNER JOIN minilinux_branch b USING (branchid)
@@ -195,17 +219,17 @@ class MiniLinux
WHERE versionid = :versionid',
['versionid' => $versionid]);
if ($ver === false)
- return false;
+ return null;
$taskid = self::validateDownloadTask($versionid, $ver['taskid']);
- if ($taskid !== false)
+ if ($taskid !== null)
return $taskid;
$data = json_decode($ver['data'], true);
if (!is_array($data)) {
EventLog::warning("Cannot download Linux '$versionid': Corrupted meta data.", $ver['data']);
- return false;
+ return null;
}
if (empty($data['files']))
- return false;
+ return null;
$list = [];
$legacyDir = preg_replace(',^[^/]*/,', '', $versionid);
foreach ($data['files'] as $file) {
@@ -224,6 +248,7 @@ class MiniLinux
Database::exec('LOCK TABLES minilinux_version WRITE');
$aff = Database::exec('UPDATE minilinux_version SET taskid = :taskid WHERE versionid = :versionid AND taskid IS NULL',
['taskid' => $uuid, 'versionid' => $versionid]);
+ $task = false;
if ($aff > 0) {
$task = Taskmanager::submit('DownloadFiles', [
'id' => $uuid,
@@ -234,10 +259,8 @@ class MiniLinux
if (Taskmanager::isFailed($task)) {
$task = false;
} else {
- $task = $task['id'];
+ $task = (string)$task['id'];
}
- } else {
- $task = false;
}
Database::exec('UNLOCK TABLES');
if ($task !== false) {
@@ -251,7 +274,7 @@ class MiniLinux
return $task;
}
- public static function fileToId($versionid, $fileName)
+ public static function fileToId(string $versionid, string $fileName): string
{
return 'x' . substr(md5($fileName . $versionid), 0, 8);
}
@@ -264,7 +287,7 @@ class MiniLinux
* Generate messages regarding setup und update availability.
* @return bool true if severe problems were found, false otherwise
*/
- public static function generateUpdateNotice()
+ public static function generateUpdateNotice(): bool
{
// Messages in here are with module name, as required by the
// main-warning hook.
@@ -307,7 +330,7 @@ class MiniLinux
* actually installed locally.
* @return bool true if installed locally, false otherwise
*/
- public static function updateCurrentBootSetting()
+ public static function updateCurrentBootSetting(): bool
{
$default = Property::get(self::PROPERTY_DEFAULT_BOOT);
if ($default === false)
@@ -320,7 +343,7 @@ class MiniLinux
} elseif ($slashes === 1) {
// Latest from branch
$ver = Database::queryFirst('SELECT versionid, installed FROM minilinux_version
- WHERE branchid = :branchid AND installed = 1 ORDER BY dateline DESC', ['branchid' => $default]);
+ WHERE branchid = :branchid AND installed = :ok ORDER BY dateline DESC', ['branchid' => $default, 'ok' => self::INSTALL_OK]);
} else {
// Unknown
return false;
@@ -331,35 +354,36 @@ class MiniLinux
return false;
}
Property::set(self::PROPERTY_DEFAULT_BOOT_EFFECTIVE, $ver['versionid']);
- return $ver['installed'] != 0;
+ return $ver['installed'] != self::INSTALL_MISSING;
}
public static function linuxDownloadCallback($task, $versionid)
{
- self::setInstalledState($versionid, $task['statusCode'] === 'TASK_FINISHED');
+ self::setInstalledState($versionid, $task['statusCode'] === 'TASK_FINISHED' ? self::INSTALL_OK : self::INSTALL_BROKEN);
}
- public static function setInstalledState($versionid, int $installed)
+ public static function setInstalledState($versionid, int $installed): void
{
- error_log("Setting $versionid to $installed");
Database::exec('UPDATE minilinux_version SET installed = :installed WHERE versionid = :versionid', [
'versionid' => $versionid,
'installed' => $installed,
]);
- if ($installed) {
- $res = Database::queryFirst('SELECT Count(*) AS cnt FROM minilinux_version WHERE installed <> 0');
+ if ($installed === self::INSTALL_OK) {
+ $res = Database::queryFirst('SELECT Count(*) AS cnt FROM minilinux_version WHERE installed = :ok',
+ ['ok' => self::INSTALL_OK]);
if ($res['cnt'] == 1) {
self::setDefaultVersion($versionid);
}
}
}
- public static function queryAllVersionsByBranch()
+ public static function queryAllVersionsByBranch(): array
{
$list = [];
- $res = Database::simpleQuery('SELECT branchid, versionid, title, dateline, orphan, taskid, installed
+ $res = Database::simpleQuery('SELECT branchid, versionid, title, Length(description) AS desclen,
+ dateline, orphan, taskid, installed
FROM minilinux_version ORDER BY branchid, dateline, versionid');
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
$list[$row['branchid']][$row['versionid']] = $row;
}
return $list;
@@ -383,11 +407,12 @@ class MiniLinux
* Check whether an optionally required stage4 is available.
* Return true if there is no stage4, otherwise check filesystem,
* or try to request from local dnbd3-server.
+ *
* @param array $data decoded data column from minilinux_version
* @param string[] $errors in array of error messages if not available
* @return bool true if stage4 is available or none required
*/
- public static function checkStage4($data, &$errors = false)
+ public static function checkStage4(array $data, &$errors = false): bool
{
$errors = [];
$image = false;
@@ -405,12 +430,22 @@ class MiniLinux
}
if ($image === false)
return true; // No stage4
- $mask = $rid;
if ($rid === 0) {
- $mask = '*';
+ // Get latest local revision
+ foreach (glob(CONFIG_VMSTORE_DIR . '/' . $image . '.r*', GLOB_NOSORT) as $file) {
+ if (preg_match('/\.r(\d+)$/', $file, $out)) {
+ $cmp = (int)$out[1];
+ if ($cmp > $rid) {
+ $rid = $cmp;
+ }
+ }
+ }
+ }
+ if ($rid > 0 && file_exists(CONFIG_VMSTORE_DIR . '/' . $image . '.r' . $rid)
+ && !file_exists(CONFIG_VMSTORE_DIR . '/' . $image . '.r' . $rid . '.map')) {
+ // Accept if image exists locally and no map file (map file would mean incomplete)
+ return true;
}
- if (glob(CONFIG_VMSTORE_DIR . '/' . $image . '.r' . $mask, GLOB_NOSORT) !== [])
- return true; // Already exists locally
// Not found locally -- try to replicate
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($sock === false) {
@@ -459,7 +494,7 @@ class MiniLinux
/**
* Determine by which menus/locations each MiniLinux version is being used.
*/
- public static function getBootMenuUsage()
+ public static function getBootMenuUsage(): array
{
if (!Module::isAvailable('serversetup') || !class_exists('BootEntryHook'))
return [];
@@ -472,34 +507,29 @@ class MiniLinux
WHERE module = 'minilinux'
GROUP BY be.data");
$return = [];
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $usedMenuIds = [];
+ foreach ($res as $row) {
$data = json_decode($row['data'], true);
if (!isset($data['id']))
continue;
$id = self::resolveEntryId($data['id']);
- if ($id === false)
- continue;
$new = [
'entryids' => [$row['entryid']],
- 'menus' => explode(',', $row['menus']),
- 'locations' => explode(',', $row['locations']),
+ 'menus' => explode(',', $row['menus'] ?? ''),
+ 'locations' => explode(',', $row['locations'] ?? ''),
];
+ $usedMenuIds = array_merge($usedMenuIds, $new['menus']);
if (isset($return[$id])) {
$return[$id] = array_merge_recursive($return[$id], $new);
} else {
$return[$id] = $new;
}
}
- // Flatten and arrayfy the list of menu ids
- $ids = ArrayUtil::flattenByKey($return, 'menus');
- $ids = array_reduce($ids, function ($carry, $item) {
- return $carry + $item;
- }, []);
// Build id => title map for menus
$res = Database::simpleQuery("SELECT menuid, title FROM serversetup_menu m
- WHERE menuid IN (:menuid)", ['menuid' => $ids]);
+ WHERE menuid IN (:menuid)", ['menuid' => array_unique($usedMenuIds)]);
$menus = [];
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
$menus[$row['menuid']] = $row['title'];
}
// Build output array
@@ -521,16 +551,16 @@ class MiniLinux
* Take a configured versionid from a bootentry (serversetup module) and translate
* it, in case it's "default" or just a branch name.
*/
- private static function resolveEntryId($id)
+ private static function resolveEntryId(string $id): string
{
if ($id === 'default') { // Special case
$id = Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT_EFFECTIVE);
}
if (substr_count($id, '/') < 2) {
// Maybe this is a branchid, which means latest from according branch (installed only)
- $res = Database::queryFirst('SELECT versionid FROM minilinux_version WHERE branchid = :id AND installed = 1
+ $res = Database::queryFirst('SELECT versionid FROM minilinux_version WHERE branchid = :id AND installed = :ok
ORDER BY dateline DESC LIMIT 1',
- ['id' => $id]);
+ ['id' => $id, 'ok' => self::INSTALL_OK]);
if ($res !== false) {
$id = $res['versionid'];
}
@@ -538,4 +568,4 @@ class MiniLinux
return $id;
}
-} \ No newline at end of file
+}