diff options
Diffstat (limited to 'modules-available/minilinux/inc/minilinux.inc.php')
-rw-r--r-- | modules-available/minilinux/inc/minilinux.inc.php | 186 |
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 +} |