summaryrefslogtreecommitdiffstats
path: root/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php')
-rw-r--r--modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php254
1 files changed, 159 insertions, 95 deletions
diff --git a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php
index 130bb52b..e97d1389 100644
--- a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php
+++ b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php
@@ -3,17 +3,6 @@
abstract class BootEntry
{
- public function __construct($data = false)
- {
- if (is_array($data)) {
- foreach ($data as $key => $value) {
- if (property_exists($this, $key)) {
- $this->{$key} = $value;
- }
- }
- }
- }
-
public abstract function supportsMode($mode);
public abstract function toScript($failLabel, $mode);
@@ -29,18 +18,31 @@ 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
*/
- public static function fromJson($data)
+ public static function fromJson($module, $data)
{
+ if ($module{0} !== '.') {
+ // Hook from other module
+ $hook = Hook::loadSingle($module, 'ipxe-bootentry');
+ if ($hook === false) {
+ error_log('Module ' . $module . ' doesnt have an ipxe-bootentry hook');
+ return null;
+ }
+ $ret = $hook->run();
+ if (!($ret instanceof BootEntryHook))
+ return null;
+ return $ret->getBootEntry($data);
+ }
if (is_string($data)) {
$data = json_decode($data, true);
}
- if (isset($data['script'])) {
+ if ($module === '.script') {
return new CustomBootEntry($data);
}
- if (isset($data['executable'])) {
+ if ($module === '.exec') {
return new StandardBootEntry($data);
}
return null;
@@ -51,9 +53,9 @@ abstract class BootEntry
return new MenuBootEntry($menuId);
}
- public static function newStandardBootEntry($initData)
+ public static function newStandardBootEntry($initData, $efi = false, $arch = false)
{
- $ret = new StandardBootEntry($initData);
+ $ret = new StandardBootEntry($initData, $efi, $arch);
$list = [];
if ($ret->arch() !== StandardBootEntry::EFI) {
$list[] = StandardBootEntry::BIOS;
@@ -61,8 +63,9 @@ abstract class BootEntry
if ($ret->arch() === StandardBootEntry::EFI || $ret->arch() === StandardBootEntry::BOTH) {
$list[] = StandardBootEntry::EFI;
}
+ $data = $ret->toArray();
foreach ($list as $mode) {
- if (empty($ret->toArray()['executable'][$mode])) {
+ if (empty($data[$mode]['executable'])) {
error_log('Incomplete stdbot: ' . print_r($initData, true));
return null;
}
@@ -85,11 +88,11 @@ abstract class BootEntry
*/
public static function fromDatabaseId($id)
{
- $row = Database::queryFirst("SELECT data FROM serversetup_bootentry
+ $row = Database::queryFirst("SELECT module, data FROM serversetup_bootentry
WHERE entryid = :id LIMIT 1", ['id' => $id]);
if ($row === false)
return false;
- return self::fromJson($row['data']);
+ return self::fromJson($row['module'], $row['data']);
}
/**
@@ -103,7 +106,7 @@ abstract class BootEntry
$res = Database::simpleQuery("SELECT entryid, data FROM serversetup_bootentry");
$ret = [];
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $tmp = self::fromJson($row['data']);
+ $tmp = self::fromJson($row['module'], $row['data']);
if ($tmp === null)
continue;
$ret[$row['entryid']] = $tmp;
@@ -115,12 +118,15 @@ abstract class BootEntry
class StandardBootEntry extends BootEntry
{
- protected $executable;
- protected $initRd;
- protected $commandLine;
- protected $replace;
- protected $autoUnload;
- protected $resetConsole;
+ /**
+ * @var ExecData PCBIOS boot data
+ */
+ protected $pcbios;
+ /**
+ * @var ExecData same for EFI
+ */
+ protected $efi;
+
protected $arch; // Constants below
const BIOS = 'PCBIOS'; // Only valid for legacy BIOS boot
@@ -128,56 +134,104 @@ class StandardBootEntry extends BootEntry
const BOTH = 'PCBIOS-EFI'; // Supports both via distinct entry
const AGNOSTIC = 'agnostic'; // Supports both via same entry (PCBIOS entry)
- public function __construct($data = false)
+ const KEYS = ['executable', 'initRd', 'commandLine', 'replace', 'imageFree', 'autoUnload', 'resetConsole', 'dhcpOptions'];
+
+ public function __construct($data, $efi = false, $arch = false)
{
+ $this->pcbios = new ExecData();
+ $this->efi = new ExecData();
if ($data instanceof PxeSection) {
- // Gets arrayfied below
- $this->executable = $data->kernel;
- $this->initRd = [self::BIOS => $data->initrd];
- $this->commandLine = ' ' . str_replace('vga=current', '', $data->append) . ' ';
- $this->resetConsole = true;
- $this->replace = true;
- $this->autoUnload = true;
- if (strpos($this->commandLine, ' quiet ') !== false) {
- $this->commandLine .= ' loglevel=5 rd.systemd.show_status=auto';
+ // Import from PXELINUX menu
+ $this->fromPxeMenu($data);
+ } elseif ($data instanceof ExecData && is_string($arch)) {
+ if (!($efi instanceof ExecData)) {
+ $efi = new ExecData();
}
- if ($data->ipAppend & 1) {
- $this->commandLine .= ' ${ipappend1}';
+ $this->pcbios = $data;
+ $this->efi = $efi;
+ $this->arch = $arch;
+ } elseif (is_array($data)) {
+ // Serialized data
+ if (!isset($data['arch'])) {
+ error_log('Serialized data to StandardBootEntry doesnt contain arch: ' . json_encode($data));
+ } else {
+ $this->arch = $data['arch'];
}
- if ($data->ipAppend & 2) {
- $this->commandLine .= ' ${ipappend2}';
+ if (isset($data[self::BIOS]) || isset($data[self::EFI])) {
+ // Current format
+ $this->fromCurrentFormat($data);
+ } else {
+ // Convert legacy DB format
+ $this->fromLegacyFormat($data);
}
- if ($data->ipAppend & 4) {
- $this->commandLine .= ' SYSUUID=${uuid}';
- }
- $this->commandLine = trim(preg_replace('/\s+/', ' ', $this->commandLine));
} else {
- if (isset($data['initRd']) && is_array($data['initRd'])) {
- foreach ($data['initRd'] as &$initrd) {
- if (is_string($initrd)) {
- $initrd = preg_split('/\s*,\s*/', $initrd);
- }
- }
- unset($initrd);
+ error_log('Invalid StandardBootEntry constructor call');
+ }
+ if (!in_array($this->arch, [self::BIOS, self::EFI, self::BOTH, self::AGNOSTIC])) {
+ $this->arch = self::AGNOSTIC;
+ }
+ }
+
+ private function fromLegacyFormat($data)
+ {
+ $ok = false;
+ foreach (self::KEYS as $key) {
+ if (isset($data[$key][self::BIOS])) {
+ $this->pcbios->{$key} = $data[$key][self::BIOS];
+ $ok = true;
+ }
+ if (isset($data[$key][self::EFI])) {
+ $this->efi->{$key} = $data[$key][self::EFI];
+ $ok = true;
}
- parent::__construct($data);
}
- // Convert legacy DB format
- foreach (['executable', 'initRd', 'commandLine', 'replace', 'autoUnload', 'resetConsole'] as $key) {
- if (!is_array($this->{$key})) {
- $this->{$key} = [ self::BIOS => $this->{$key}, self::EFI => '' ];
+ if (!$ok) {
+ // Very old entry
+ foreach (self::KEYS as $key) {
+ if (isset($data[$key])) {
+ $this->pcbios->{$key} = $data[$key];
+ }
}
}
- foreach ($this->initRd as &$initrd) {
- if (!is_array($initrd)) {
- $initrd = [$initrd];
+ }
+
+ private function fromCurrentFormat($data)
+ {
+ foreach (self::KEYS as $key) {
+ if (isset($data[self::BIOS][$key])) {
+ $this->pcbios->{$key} = $data[self::BIOS][$key];
+ }
+ if (isset($data[self::EFI][$key])) {
+ $this->efi->{$key} = $data[self::EFI][$key];
}
- $initrd = array_filter($initrd, function($x) { return strlen(trim($x)) !== 0; });
}
- unset($initrd);
- if ($this->arch === null) {
- $this->arch = self::AGNOSTIC;
+ }
+
+ /**
+ * @param PxeSection $data
+ */
+ private function fromPxeMenu($data)
+ {
+ $bios = $this->pcbios;
+ $bios->executable = $data->kernel;
+ $bios->initRd = $data->initrd;
+ $bios->commandLine = ' ' . str_replace('vga=current', '', $data->append) . ' ';
+ $bios->resetConsole = true;
+ $bios->replace = true;
+ $bios->autoUnload = true;
+ if (strpos($bios->commandLine, ' quiet ') !== false) {
+ $bios->commandLine .= ' loglevel=5 rd.systemd.show_status=auto';
+ }
+ if ($data->ipAppend & 1) {
+ $bios->commandLine .= ' ${ipappend1}';
}
+ if ($data->ipAppend & 2) {
+ $bios->commandLine .= ' ${ipappend2}';
+ }
+ if ($data->ipAppend & 4) {
+ $bios->commandLine .= ' SYSUUID=${uuid}';
+ }
+ $bios->commandLine = trim(preg_replace('/\s+/', ' ', $bios->commandLine));
}
public function arch()
@@ -201,19 +255,37 @@ class StandardBootEntry extends BootEntry
if (!$this->supportsMode($mode)) {
return "prompt Entry doesn't have an executable for mode $mode\n";
}
- if ($this->arch === self::AGNOSTIC) {
- $mode = self::BIOS;
+ if ($this->arch === self::AGNOSTIC || $mode == self::BIOS) {
+ $entry = $this->pcbios;
+ } else {
+ $entry = $this->efi;
}
$script = '';
- if ($this->resetConsole[$mode]) {
+ if ($entry->resetConsole) {
$script .= "console ||\n";
}
- // TODO: Checkbox
- $script .= "imgfree ||\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($this->initRd[$mode])) {
- foreach (array_values($this->initRd[$mode]) as $i => $initrd) {
+ 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";
@@ -221,23 +293,23 @@ class StandardBootEntry extends BootEntry
}
}
$script .= "boot ";
- if ($this->autoUnload[$mode]) {
+ if ($entry->autoUnload) {
$script .= "-a ";
}
- if ($this->replace[$mode]) {
+ if ($entry->replace) {
$script .= "-r ";
}
- $script .= $this->executable[$mode];
+ $script .= $entry->executable;
if (empty($initrds)) {
$rdBase = '';
} else {
$rdBase = " initrd=" . implode(',', $initrds);
}
- if (!empty($this->commandLine[$mode])) {
- $script .= "$rdBase {$this->commandLine[$mode]}";
+ if (!empty($entry->commandLine)) {
+ $script .= "$rdBase {$entry->commandLine}";
}
$script .= " || goto $failLabel\n";
- if ($this->resetConsole[$mode]) {
+ if ($entry->resetConsole) {
$script .= "goto start ||\n";
}
return $script;
@@ -246,30 +318,16 @@ class StandardBootEntry extends BootEntry
public function addFormFields(&$array)
{
$array[$this->arch . '_selected'] = 'selected';
- foreach ([self::BIOS, self::EFI] as $mode) {
- $array['entries'][] = [
- 'is' . $mode => true,
- 'mode' => $mode,
- 'executable' => $this->executable[$mode],
- 'initRd' => implode(',', $this->initRd[$mode]),
- 'commandLine' => $this->commandLine[$mode],
- 'replace_checked' => $this->replace[$mode] ? 'checked' : '',
- 'autoUnload_checked' => $this->autoUnload[$mode] ? 'checked' : '',
- 'resetConsole_checked' => $this->resetConsole[$mode] ? 'checked' : '',
- ];
- }
+ $array['entries'][] = $this->pcbios->toFormFields(self::BIOS);
+ $array['entries'][] = $this->efi->toFormFields(self::EFI);
$array['exec_checked'] = 'checked';
}
public function toArray()
{
return [
- 'executable' => $this->executable,
- 'initRd' => $this->initRd,
- 'commandLine' => $this->commandLine,
- 'replace' => $this->replace,
- 'autoUnload' => $this->autoUnload,
- 'resetConsole' => $this->resetConsole,
+ self::BIOS => $this->pcbios->toArray(),
+ self::EFI => $this->efi->toArray(),
'arch' => $this->arch,
];
}
@@ -279,6 +337,13 @@ class CustomBootEntry extends BootEntry
{
protected $script;
+ public function __construct($data)
+ {
+ if (is_array($data) && isset($data['script'])) {
+ $this->script = $data['script'];
+ }
+ }
+
public function supportsMode($mode)
{
return true;
@@ -309,7 +374,6 @@ class MenuBootEntry extends BootEntry
public function __construct($menuId)
{
- parent::__construct(false);
$this->menuId = $menuId;
}