diff options
Diffstat (limited to 'modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php')
-rw-r--r-- | modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php | 248 |
1 files changed, 130 insertions, 118 deletions
diff --git a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php index e89380ce..5812c0cd 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php @@ -12,13 +12,28 @@ abstract class BootEntry /** Supports both via distinct entry */ const BOTH = 'PCBIOS-EFI'; - public abstract function supportsMode($mode); + /** + * @var string Internal ID - set to your liking, e.g. the MiniLinux version identifier + */ + protected $internalId; + + public function __construct(string $internalId) + { + $this->internalId = $internalId; + } - public abstract function toScript($failLabel, $mode); + public abstract function supportsMode(string $mode): bool; - public abstract function toArray(); + public abstract function toScript(ScriptBuilderBase $builder): string; - public abstract function addFormFields(&$array); + public abstract function toArray(): array; + + public abstract function addFormFields(array &$array): void; + + public function internalId(): string + { + return $this->internalId; + } /* * @@ -28,15 +43,15 @@ abstract class BootEntry * Return a BootEntry instance from the serialized data. * * @param string $module module this entry belongs to, or special values .script/.exec - * @param string $jsonString serialized entry data - * @return BootEntry|null instance representing boot entry, null on error + * @param string $data serialized entry data + * @return ?BootEntry instance representing boot entry, null on error */ - public static function fromJson($module, $data) + public static function fromJson(string $module, string $data): ?BootEntry { - if ($module{0} !== '.') { + if ($module[0] !== '.') { // Hook from other module $hook = Hook::loadSingle($module, 'ipxe-bootentry'); - if ($hook === false) { + if ($hook === null) { error_log('Module ' . $module . ' doesnt have an ipxe-bootentry hook'); return null; } @@ -45,26 +60,29 @@ abstract class BootEntry return null; return $ret->getBootEntry($data); } - if (is_string($data)) { - $data = json_decode($data, true); - } + $data = json_decode($data, true); + if (!is_array($data)) + return null; if ($module === '.script') { return new CustomBootEntry($data); } if ($module === '.exec') { return new StandardBootEntry($data); } + if ($module === '.special') { + return new SpecialBootEntry($data); + } return null; } - public static function forMenu($menuId) + public static function forMenu(int $menuId): MenuBootEntry { return new MenuBootEntry($menuId); } - public static function newStandardBootEntry($initData, $efi = false, $arch = false) + public static function newStandardBootEntry($initData, $efi = false, $arch = false, string $internalId = ''): ?StandardBootEntry { - $ret = new StandardBootEntry($initData, $efi, $arch); + $ret = new StandardBootEntry($initData, $efi, $arch, $internalId); $list = []; if ($ret->arch() !== self::EFI) { $list[] = self::BIOS; @@ -82,9 +100,9 @@ abstract class BootEntry return $ret; } - public static function newCustomBootEntry($initData) + public static function newCustomBootEntry($initData): ?CustomBootEntry { - if (empty($initData['script'])) + if (!is_array($initData) || empty($initData)) return null; return new CustomBootEntry($initData); } @@ -92,15 +110,14 @@ abstract class BootEntry /** * Return a BootEntry instance from database with the given id. * - * @param string $id - * @return BootEntry|null|false false == unknown id, null = unknown entry type, BootEntry instance on success + * @return ?BootEntry null = unknown entry type, BootEntry instance on success */ - public static function fromDatabaseId($id) + public static function fromDatabaseId(string $id): ?BootEntry { $row = Database::queryFirst("SELECT module, data FROM serversetup_bootentry WHERE entryid = :id LIMIT 1", ['id' => $id]); if ($row === false) - return false; + return null; return self::fromJson($row['module'], $row['data']); } @@ -110,11 +127,11 @@ abstract class BootEntry * * @return BootEntry[] all existing BootEntries */ - public static function getAll() + public static function getAll(): array { - $res = Database::simpleQuery("SELECT entryid, data FROM serversetup_bootentry"); + $res = Database::simpleQuery("SELECT entryid, module, data FROM serversetup_bootentry"); $ret = []; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { $tmp = self::fromJson($row['module'], $row['data']); if ($tmp === null) continue; @@ -135,13 +152,16 @@ class StandardBootEntry extends BootEntry * @var ExecData same for EFI */ protected $efi; - - protected $arch; // Constants below + /** + * @var ?string BootEntry Constants above + */ + protected $arch; const KEYS = ['executable', 'initRd', 'commandLine', 'replace', 'imageFree', 'autoUnload', 'resetConsole', 'dhcpOptions']; - public function __construct($data, $efi = false, $arch = false) + public function __construct($data, $efi = false, ?string $arch = null, string $internalId = '') { + parent::__construct($internalId); $this->pcbios = new ExecData(); $this->efi = new ExecData(); if ($data instanceof PxeSection) { @@ -214,10 +234,7 @@ class StandardBootEntry extends BootEntry } } - /** - * @param PxeSection $data - */ - private function fromPxeMenu($data) + private function fromPxeMenu(PxeSection $data): void { $bios = $this->pcbios; $bios->executable = $data->kernel; @@ -241,12 +258,12 @@ class StandardBootEntry extends BootEntry $bios->commandLine = trim(preg_replace('/\s+/', ' ', $bios->commandLine)); } - public function arch() + public function arch(): ?string { return $this->arch; } - public function supportsMode($mode) + public function supportsMode(string $mode): bool { if ($mode === $this->arch || $this->arch === BootEntry::AGNOSTIC) return true; @@ -257,77 +274,16 @@ class StandardBootEntry extends BootEntry return false; } - public function toScript($failLabel, $mode) + public function toScript(ScriptBuilderBase $builder): string { - if (!$this->supportsMode($mode)) { - return "prompt Entry doesn't have an executable for mode $mode\n"; - } - if ($this->arch === BootEntry::AGNOSTIC || $mode == BootEntry::BIOS) { - $entry = $this->pcbios; - } else { - $entry = $this->efi; - } - $entry->sanitize(); - - $script = ''; - if ($entry->resetConsole) { - $script .= "console ||\n"; - } - if ($entry->imageFree) { - $script .= "imgfree ||\n"; - } - foreach ($entry->dhcpOptions as $opt) { - if (empty($opt['value'])) { - $val = '${}'; - } else { - if (empty($opt['hex'])) { - $val = bin2hex($opt['value']); - } else { - $val = $opt['value']; - } - preg_match_all('/[0-9a-f]{2}/', $val, $out); - $val = implode(':', $out[0]); - } - $script .= 'set net${idx}/' . $opt['opt'] . ':hex ' . $val - . ' || prompt Cannot override DHCP server option ' . $opt['opt'] . ". Press any key to continue anyways.\n"; - } - $initrds = []; - if (!empty($entry->initRd)) { - foreach (array_values($entry->initRd) as $i => $initrd) { - if (empty($initrd)) - continue; - $script .= "initrd --name initrd$i $initrd || goto $failLabel\n"; - $initrds[] = "initrd$i"; - } - } - $script .= "boot "; - if ($entry->autoUnload) { - $script .= "-a "; - } - if ($entry->replace) { - $script .= "-r "; - } - $script .= $entry->executable; - if (!empty($initrds)) { - if ($mode === BootEntry::BIOS) { - $script .= " initrd=" . implode(',', $initrds); - } else { - foreach ($initrds as $initrd) { - $script .= " initrd=$initrd"; - } - } - } - if (!empty($entry->commandLine)) { - $script .= ' ' . $entry->commandLine; - } - $script .= " || goto $failLabel\n"; - if ($entry->resetConsole) { - $script .= "goto start ||\n"; - } - return $script; + if ($this->arch === BootEntry::AGNOSTIC) // Same as below, could construct fall-through but this is more clear + return $builder->execDataToScript($this->pcbios, null, null); + return $builder->execDataToScript(null, + $this->supportsMode(BootEntry::BIOS) ? $this->pcbios : null, + $this->supportsMode(BootEntry::EFI) ? $this->efi : null); } - public function addFormFields(&$array) + public function addFormFields(array &$array): void { $array[$this->arch . '_selected'] = 'selected'; $array['entries'][] = $this->pcbios->toFormFields(BootEntry::BIOS); @@ -335,7 +291,10 @@ class StandardBootEntry extends BootEntry $array['exec_checked'] = 'checked'; } - public function toArray() + /** + * @return array{PCBIOS: array, EFI: array, arch: string} + */ + public function toArray(): array { return [ BootEntry::BIOS => $this->pcbios->toArray(), @@ -347,65 +306,118 @@ class StandardBootEntry extends BootEntry class CustomBootEntry extends BootEntry { - protected $script; + /** + * @var string iPXE + */ + protected $ipxe = ''; + + protected $bash; + + protected $grub; public function __construct($data) { - if (is_array($data) && isset($data['script'])) { - $this->script = $data['script']; + parent::__construct('custom'); + if (is_array($data)) { + $this->ipxe = $data['script'] ?? ''; // LEGACY + foreach (['bash', 'grub'] as $key) { + $this->{$key} = $data[$key] ?? ''; + } } } - public function supportsMode($mode) + public function supportsMode(string $mode): bool { return true; } - public function toScript($failLabel, $mode) + public function toScript(ScriptBuilderBase $builder): string { - return str_replace('%fail%', $failLabel, $this->script) . "\n"; + // TODO: A (very) simple translator for oneliners like "poweroff || goto fail" maybe? + if ($builder instanceof ScriptBuilderIpxe) + return $this->ipxe; + if ($builder instanceof ScriptBuilderBash) + return $this->bash; + if ($builder instanceof ScriptBuilderGrub) + return $this->grub; + return ''; } - public function addFormFields(&$array) + public function addFormFields(array &$array): void { $array['entry'] = [ - 'script' => $this->script, + 'script' => $this->ipxe, ]; $array['script_checked'] = 'checked'; } - public function toArray() + /** + * @return array{script: string} + */ + public function toArray(): array { - return ['script' => $this->script]; + return ['script' => $this->ipxe]; } } class MenuBootEntry extends BootEntry { + /** @var int */ protected $menuId; - public function __construct($menuId) + public function __construct(int $menuId) { + parent::__construct('menu-' . $menuId); $this->menuId = $menuId; } - public function supportsMode($mode) + public function supportsMode(string $mode): bool { return true; } - public function toScript($failLabel, $mode) + public function toScript(ScriptBuilderBase $builder): string { - return 'chain -ar ${self}&menuid=' . $this->menuId . ' || goto ' . $failLabel . "\n"; + $menu = IPxeMenu::get($this->menuId, true); + return $builder->menuToScript($menu); } - public function toArray() + public function toArray(): array { return []; } - public function addFormFields(&$array) + public function addFormFields(array &$array): void { } } +class SpecialBootEntry extends BootEntry +{ + + private $type; + + public function __construct($type) + { + $this->type = $type['type'] ?? $type; + parent::__construct('special-' . $this->type); + } + + public function supportsMode(string $mode): bool + { + return true; + } + + public function toScript(ScriptBuilderBase $builder): string + { + return $builder->getSpecial($this->type); + } + + public function toArray(): array + { + return []; + } + + public function addFormFields(array &$array): void { } + +} |