diff options
Diffstat (limited to 'modules-available/minilinux/inc/linuxbootentryhook.inc.php')
-rw-r--r-- | modules-available/minilinux/inc/linuxbootentryhook.inc.php | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/modules-available/minilinux/inc/linuxbootentryhook.inc.php b/modules-available/minilinux/inc/linuxbootentryhook.inc.php new file mode 100644 index 00000000..1424b6b9 --- /dev/null +++ b/modules-available/minilinux/inc/linuxbootentryhook.inc.php @@ -0,0 +1,193 @@ +<?php + +/** + * Class LinuxBootEntryHook. + * Only to be used in the ipxe-bootentry hook, as this depends on + * the existence of BootEntryHook, a class from serversetup-bwlp-ipxe. + * This module is usually not activated when interacting with the + * minilinux module. + */ +class LinuxBootEntryHook extends BootEntryHook +{ + + public function name(): string + { + return Dictionary::translateFileModule('minilinux', 'module', 'module_name'); + } + + public function extraFields(): array + { + /* For translate module: + * Dictionary::translate('ipxe-kcl-extra'); + * Dictionary::translate('ipxe-debug'); + * Dictionary::translate('ipxe-insecure-cpu'); + * Dictionary::translate('ipxe-force-init-dhcp'); + */ + return [ + new HookExtraField('kcl-extra', 'string', ''), + new HookExtraField('debug', 'bool', false), + new HookExtraField('insecure-cpu', 'bool', false), + new HookExtraField('force-init-dhcp', 'bool', false), + ]; + } + + /** + * @return HookEntryGroup[] + */ + protected function groupsInternal(): array + { + /* + * Dictionary::translate('default_boot_entry'); + * Dictionary::translate('not_installed_hint'); + * Dictionary::translate('latest_of_branch'); + */ + $array = []; + $array[] = new HookEntryGroup($this->name(), [ + new HookEntry('default', + Dictionary::translateFileModule('minilinux', 'module', 'default_boot_entry'), + MiniLinux::updateCurrentBootSetting()) + ]); + $branches = Database::queryAll('SELECT sourceid, branchid, title FROM minilinux_branch ORDER BY title'); + $versions = MiniLinux::queryAllVersionsByBranch(); + // Group by branch for detailed listing + foreach ($branches as $branch) { + if (isset($versions[$branch['branchid']])) { + $group = [ + new HookEntry($branch['branchid'], + $branch['branchid'] . ' ' + . Dictionary::translateFileModule('minilinux', 'module', + 'latest_of_branch'), + true), + ]; + foreach ($versions[$branch['branchid']] as $version) { + $valid = $version['installed'] != MiniLinux::INSTALL_MISSING; + $title = $version['versionid'] . ' ' . $version['title']; + if (!$valid) { + $title .= ' ' . Dictionary::translateFileModule('minilinux', 'module', + 'not_installed_hint'); + } + $group[] = new HookEntry($version['versionid'], $title, $valid); + } + $array[] = new HookEntryGroup($branch['title'] ?: $branch['branchid'], $group); + } + } + return $array; + } + + /** + * @return ?BootEntry the actual boot entry instance for given entry, false if invalid id + */ + public function getBootEntryInternal(array $localData): ?BootEntry + { + $id = $localData['id']; + if ($id === 'default') { // Special case + $effectiveId = Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT_EFFECTIVE); + } else { + $effectiveId = $id; + } + $res = Database::queryFirst('SELECT versionid, installed, data FROM minilinux_version WHERE versionid = :id', + ['id' => $effectiveId]); + if ($res === false) { + // Maybe this is a branchid, which means latest from according branch (installed only) + $res = Database::queryFirst('SELECT versionid, installed, data FROM minilinux_version + WHERE branchid = :id AND installed = :ok + ORDER BY dateline DESC LIMIT 1', + ['id' => $effectiveId, 'ok' => MiniLinux::INSTALL_OK]); + } + if ($res === false) { + return BootEntry::newCustomBootEntry(['script' => 'prompt Selected version not currently installed on server: ' . $effectiveId]); + } + $effectiveId = $res['versionid']; // In case we selected from a branchid, so above message doesn't show versionid + $remoteData = json_decode($res['data'], true); + $bios = $efi = false; + if (!@is_array($remoteData['agnostic']) && !@is_array($remoteData['efi']) && !@is_array($remoteData['bios'])) { + $remoteData['agnostic'] = []; // We got nothing at all so fake this entry, resulting in a generic default entry + } + if (@is_array($remoteData['agnostic'])) { + $bios = $this->generateExecData($effectiveId, $remoteData['agnostic'], $localData); + $arch = BootEntry::AGNOSTIC; + } else { + if (@is_array($remoteData['efi'])) { + $efi = $this->generateExecData($effectiveId, $remoteData['efi'], $localData); + } + if (@is_array($remoteData['bios'])) { + $bios = $this->generateExecData($effectiveId, $remoteData['bios'], $localData); + } + if ($bios && $efi) { + $arch = BootEntry::BOTH; + } elseif ($bios) { + $arch = BootEntry::BIOS; + } else { + $arch = BootEntry::EFI; + } + } + return BootEntry::newStandardBootEntry($bios, $efi, $arch, 'ml-' . $id); + } + + private function generateExecData($effectiveId, $remoteData, $localData): ExecData + { + $exec = new ExecData(); + // Defaults + $root = '/boot/' . $effectiveId . '/'; + $exec->executable = 'kernel'; + $exec->initRd = ['initramfs-stage31']; + $exec->imageFree = true; + $exec->commandLine = 'slxbase=boot/%ID% slxsrv=${serverip} quiet splash ${ipappend1} ${ipappend2}' + . ' ipv4.ip=${ip} ipv4.router=${gateway} ipv4.dns=${dns} ipv4.hostname=${hostname} ipv4.domain=${domain} ipv4.search=${dnssl}' + . ' ipv4.if=${mac} ipv4.ntpsrv=${ntpsrv} ipv4.subnet=${netmask}'; + // Overrides + foreach (['executable', 'commandLine', 'initRd', 'imageFree'] as $key) { + if (isset($remoteData[$key])) { + $exec->{$key} = $remoteData[$key]; + } + } + // KCL hacks + if (!empty($localData['debug'])) { + // Debug boot enabled + $exec->commandLine = IPxe::modifyCommandLine($exec->commandLine, + $remoteData['debugCommandLineModifier'] ?? '-vga -quiet -splash -loglevel loglevel=7' + ); + } + // disable all CPU sidechannel attack mitigations etc. + if (!empty($localData['insecure-cpu'])) { + $exec->commandLine = IPxe::modifyCommandLine($exec->commandLine, + 'noibrs noibpb nopti nospectre_v2 nospectre_v1 l1tf=off nospec_store_bypass_disable no_stf_barrier mds=off mitigations=off i915.mitigations=off'); + } + // force that we + if (!empty($localData['force-init-dhcp'])) { + $exec->commandLine = IPxe::modifyCommandLine($exec->commandLine, + '-ipv4.router -ipv4.dns -ipv4.subnet'); + } + // GVT, PCI Pass-thru etc. + if (Module::isAvailable('statistics')) { + $hwextra = HardwareInfo::getKclModifications(); + if (!empty($hwextra)) { + $exec->commandLine = IPxe::modifyCommandLine($exec->commandLine, $hwextra); + } + } + // User-supplied modifications + if (!empty($localData['kcl-extra'])) { + $exec->commandLine = IPxe::modifyCommandLine($exec->commandLine, $localData['kcl-extra']); + } + $exec->commandLine = str_replace('%ID%', $effectiveId, $exec->commandLine); + $exec->executable = $root . $exec->executable; + foreach ($exec->initRd as &$rd) { + if ($rd[0] !== '/') { + $rd = $root . $rd; + } + } + unset($rd); + return $exec; + } + + public function isValidId(string $id): bool + { + if ($id === 'default') + return true; // Meta-version that links to whatever the default is set to + $res = Database::queryFirst('SELECT installed FROM minilinux_version WHERE versionid = :id', ['id' => $id]); + if ($res !== false && $res['installed'] != MiniLinux::INSTALL_MISSING) + return true; + $res = Database::queryFirst('SELECT branchid FROM minilinux_branch WHERE branchid = :id', ['id' => $id]); + return $res !== false; + } +} |