From c2a6dfb649107ce3ad64162e2bb2c3c7275650fb Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 12 Feb 2019 14:55:12 +0100 Subject: [serversetup*] PXELinux and iPXE side-by-side --- .../serversetup-bwlp/inc/ipxe.inc.php | 453 --------------------- 1 file changed, 453 deletions(-) delete mode 100644 modules-available/serversetup-bwlp/inc/ipxe.inc.php (limited to 'modules-available/serversetup-bwlp/inc/ipxe.inc.php') diff --git a/modules-available/serversetup-bwlp/inc/ipxe.inc.php b/modules-available/serversetup-bwlp/inc/ipxe.inc.php deleted file mode 100644 index d34839f0..00000000 --- a/modules-available/serversetup-bwlp/inc/ipxe.inc.php +++ /dev/null @@ -1,453 +0,0 @@ -= :start AND endaddr <= :end", compact('start', 'end')); - $locations = []; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - foreach ($locations as &$loc) { - if ($row['startaddr'] <= $loc['startaddr'] && $row['endaddr'] >= $loc['endaddr']) { - $loc = false; - } elseif ($row['startaddr'] >= $loc['startaddr'] && $row['endaddr'] <= $loc['endaddr']) { - continue 2; - } - } - unset($loc); - $locations[] = $row; - } - $menu = PxeLinux::parsePxeLinux($content); - $key = $menu->hash(true); - if (isset($menus[$key])) { - $menuId = $menus[$key]; - $defId = null; - // Figure out the default label, get it's label name - foreach ($menu->sections as $section) { - if ($section->isDefault) { - $defId = $section; - } elseif ($defId === null && $section->label === $menu->timeoutLabel) { - $defId = $section; - } - } - if ($defId !== null) { - $defId = self::cleanLabelFixLocal($defId); - // Confirm it actually exists (it should since the menu seems identical) and get menuEntryId - $me = Database::queryFirst('SELECT m.defaultentryid, me.menuentryid FROM serversetup_bootentry be - INNER JOIN serversetup_menuentry me ON (be.entryid = me.entryid) - INNER JOIN serversetup_menu m ON (m.menuid = me.menuid) - WHERE be.entryid = :id AND me.menuid = :menuid', - ['id' => $defId, 'menuid' => $menuId]); - if ($me === false || $me['defaultentryid'] == $me['menuentryid']) { - $defId = null; // Not found, or is already default - don't override if it's the same - } else { - $defId = $me['menuentryid']; - } - } - } else { - $menuId = self::insertMenu($menu, 'Imported', false, 0, [], []); - $menus[$key] = $menuId; - $defId = null; - $importCount++; - } - if ($menuId === false) - continue; - foreach ($locations as $loc) { - if ($loc === false) - continue; - Database::exec('INSERT IGNORE INTO serversetup_menu_location (menuid, locationid, defaultentryid) - VALUES (:menuid, :locationid, :def)', [ - 'menuid' => $menuId, - 'locationid' => $loc['locationid'], - 'def' => $defId, - ]); - } - } - return $importCount; - } - - public static function importLegacyMenu($force = false) - { - if (!$force && false !== Database::queryFirst("SELECT entryid FROM serversetup_bootentry WHERE entryid = 'bwlp-default'")) - return false; // Already exists - // Now create the default entry - self::createDefaultEntries(); - $prepend = ['bwlp-default' => false, 'localboot' => false]; - $defaultLabel = 'bwlp-default'; - $menuTitle = 'bwLehrpool Bootauswahl'; - $pxeConfig = ''; - $timeoutSecs = 60; - // Try to import any customization - $oldMenu = Property::getBootMenu(); - if (is_array($oldMenu)) { - // - if (isset($oldMenu['timeout'])) { - $timeoutSecs = (int)$oldMenu['timeout']; - } - if (isset($oldMenu['defaultentry'])) { - if ($oldMenu['defaultentry'] === 'net') { - $defaultLabel = 'bwlp-default'; - } elseif ($oldMenu['defaultentry'] === 'hdd') { - $defaultLabel = 'localboot'; - } elseif ($oldMenu['defaultentry'] === 'custom') { - $defaultLabel = 'custom'; - } - } - if (!empty($oldMenu['custom'])) { - $pxeConfig = $oldMenu['custom']; - } - } - $append = [ - '', - 'bwlp-default-dbg' => false, - '', - 'poweroff' => false, - ]; - return self::insertMenu(PxeLinux::parsePxeLinux($pxeConfig), $menuTitle, $defaultLabel, $timeoutSecs, $prepend, $append); - } - - /** - * @param PxeMenu $pxeMenu - * @param string $menuTitle - * @param string|false $defaultLabel - * @param $defaultTimeoutSeconds - * @param $prepend - * @param $append - * @return bool|int - */ - private static function insertMenu($pxeMenu, $menuTitle, $defaultLabel, $defaultTimeoutSeconds, $prepend, $append) - { - $timeoutMs = []; - $menuEntries = $prepend; - settype($menuEntries, 'array'); - if (!empty($pxeMenu)) { - $pxe =& $pxeMenu; - if (!empty($pxe->title)) { - $menuTitle = $pxe->title; - } - if ($pxe->timeoutLabel !== null) { - $defaultLabel = $pxe->timeoutLabel; - } - $timeoutMs[] = $pxe->timeoutMs; - $timeoutMs[] = $pxe->totalTimeoutMs; - foreach ($pxe->sections as $section) { - if ($section->localBoot || preg_match('/chain\.c32$/i', $section->kernel)) { - $menuEntries['localboot'] = 'localboot'; - continue; - } - if ($section->label === null) { - if (!$section->isHidden && !empty($section->title)) { - $menuEntries[] = $section->title; - } - continue; - } - if (empty($section->kernel)) { - if (!$section->isHidden && !empty($section->title)) { - $menuEntries[] = $section->title; - } - continue; - } - $entry = self::pxe2BootEntry($section); - if ($entry === null) - continue; - $label = self::cleanLabelFixLocal($section); - if ($defaultLabel === $section->label) { - $defaultLabel = $label; - } - $hotkey = MenuEntry::filterKeyName($section->hotkey); - // Create boot entry - $data = $entry->toArray(); - Database::exec('INSERT IGNORE INTO serversetup_bootentry (entryid, hotkey, title, builtin, data) - VALUES (:label, :hotkey, :title, 0, :data)', [ - 'label' => $label, - 'hotkey' => $hotkey, - 'title' => self::sanitizeIpxeString($section->title), - 'data' => json_encode($data), - ]); - $menuEntries[$label] = $section; - } - } - if (is_array($append)) { - $menuEntries += $append; - } - if (empty($menuEntries)) - return false; - // Make menu - $timeoutMs = array_filter($timeoutMs, 'is_int'); - if (empty($timeoutMs)) { - $timeoutMs = (int)($defaultTimeoutSeconds * 1000); - } else { - $timeoutMs = min($timeoutMs); - } - $isDefault = (int)(Database::queryFirst('SELECT menuid FROM serversetup_menu WHERE isdefault = 1') === false); - Database::exec("INSERT INTO serversetup_menu (timeoutms, title, defaultentryid, isdefault) - VALUES (:timeoutms, :title, NULL, :isdefault)", [ - 'title' => self::sanitizeIpxeString($menuTitle), - 'timeoutms' => $timeoutMs, - 'isdefault' => $isDefault, - ]); - $menuId = Database::lastInsertId(); - if (!array_key_exists($defaultLabel, $menuEntries) && $timeoutMs > 0) { - $defaultLabel = array_keys($menuEntries)[0]; - } - // Link boot entries to menu - $defaultEntryId = null; - $order = 1000; - foreach ($menuEntries as $label => $entry) { - if (is_string($entry)) { - // Gap entry - Database::exec("INSERT INTO serversetup_menuentry - (menuid, entryid, hotkey, title, hidden, sortval, plainpass, md5pass) - VALUES (:menuid, :entryid, :hotkey, :title, :hidden, :sortval, '', '')", [ - 'menuid' => $menuId, - 'entryid' => null, - 'hotkey' => '', - 'title' => self::sanitizeIpxeString($entry), - 'hidden' => 0, - 'sortval' => $order += 100, - ]); - continue; - } - $data = Database::queryFirst("SELECT entryid, hotkey, title FROM serversetup_bootentry WHERE entryid = :entryid", ['entryid' => $label]); - if ($data === false) - continue; - $data['pass'] = ''; - $data['hidden'] = 0; - if ($entry instanceof PxeSection) { - $data['hidden'] = (int)$entry->isHidden; - // Prefer explicit data from this imported menu over the defaults - $data['title'] = self::sanitizeIpxeString($entry->title); - if (MenuEntry::getKeyCode($entry->hotkey) !== false) { - $data['hotkey'] = $entry->hotkey; - } - if (!empty($entry->passwd)) { - // Most likely it's a hash so we cannot recover; ask people to reset - $data['pass'] ='please_reset'; - } - } - $data['menuid'] = $menuId; - $data['sortval'] = $order += 100; - $res = Database::exec("INSERT INTO serversetup_menuentry - (menuid, entryid, hotkey, title, hidden, sortval, plainpass, md5pass) - VALUES (:menuid, :entryid, :hotkey, :title, :hidden, :sortval, :pass, :pass)", $data); - if ($res !== false && $label === $defaultLabel) { - $defaultEntryId = Database::lastInsertId(); - } - } - // Now we can set default entry - if (!empty($defaultEntryId)) { - Database::exec("UPDATE serversetup_menu SET defaultentryid = :entryid WHERE menuid = :menuid", - ['menuid' => $menuId, 'entryid' => $defaultEntryId]); - } - // TODO: masterpw? rather pointless.... - //$oldMenu['masterpasswordclear']; - return $menuId; - } - - private static function createDefaultEntries() - { - $query = 'INSERT IGNORE INTO serversetup_bootentry (entryid, hotkey, title, builtin, data) - VALUES (:entryid, :hotkey, :title, 1, :data)'; - Database::exec($query, - [ - 'entryid' => 'bwlp-default', - 'hotkey' => 'B', - 'title' => 'bwLehrpool-Umgebung starten', - 'data' => json_encode([ - 'executable' => '/boot/default/kernel', - 'initRd' => '/boot/default/initramfs-stage31', - 'commandLine' => 'slxbase=boot/default quiet splash loglevel=5 rd.systemd.show_status=auto intel_iommu=igfx_off ${ipappend1} ${ipappend2}', - 'replace' => true, - 'autoUnload' => true, - 'resetConsole' => true, - ]), - ]); - Database::exec($query, - [ - 'entryid' => 'bwlp-default-dbg', - 'hotkey' => '', - 'title' => 'bwLehrpool-Umgebung starten (nosplash, debug)', - 'data' => json_encode([ - 'executable' => '/boot/default/kernel', - 'initRd' => '/boot/default/initramfs-stage31', - 'commandLine' => 'slxbase=boot/default loglevel=7 intel_iommu=igfx_off ${ipappend1} ${ipappend2}', - 'replace' => true, - 'autoUnload' => true, - 'resetConsole' => true, - ]), - ]); - Database::exec($query, - [ - 'entryid' => 'localboot', - 'hotkey' => 'L', - 'title' => 'Lokales System starten', - 'data' => json_encode([ - 'script' => 'goto slx_localboot || goto %fail% ||', - ]), - ]); - Database::exec($query, - [ - 'entryid' => 'poweroff', - 'hotkey' => 'P', - 'title' => 'Power off', - 'data' => json_encode([ - 'script' => 'poweroff || goto %fail% ||', - ]), - ]); - Database::exec($query, - [ - 'entryid' => 'reboot', - 'hotkey' => 'R', - 'title' => 'Reboot', - 'data' => json_encode([ - 'script' => 'reboot || goto %fail% ||', - ]), - ]); - } - - /** - * Create unique label for a boot entry. It will try to figure out whether - * this is one of our default entries and if not, create a unique label - * representing the menu entry contents. - * Also it patches the entry if it's referencing the local bwlp install - * because side effects. - * - * @param PxeSection $section - * @return string - */ - private static function cleanLabelFixLocal($section) - { - $myip = Property::getServerIp(); - // Detect our "old" entry types - if (count($section->initrd) === 1 && preg_match(",$myip/boot/default/kernel\$,", $section->kernel) - && preg_match(",$myip/boot/default/initramfs-stage31\$,", $section->initrd[0])) { - // Kernel and initrd match, examine KCL - if ($section->append === 'slxbase=boot/default vga=current quiet splash') { - // Normal - return 'bwlp-default'; - } elseif ($section->append === 'slxbase=boot/default') { - // Debug output - return 'bwlp-default-dbg'; - } else { - // Transform to relative URL, leave KCL, fall through to generic label gen - $section->kernel = '/boot/default/kernel'; - $section->initrd = ['/boot/default/initramfs-stage31']; - } - } - // Generic -- "smart" hash of kernel, initrd and command line - $str = $section->kernel . ' ' . implode(',', $section->initrd); - $array = preg_split('/\s+/', $section->append, -1, PREG_SPLIT_NO_EMPTY); - sort($array); - $str .= ' ' . implode(' ', $array); - - return 'i-' . substr(md5($str), 0, 12); - } - - /** - * @param PxeSection $section - * @return BootEntry|null The according boot entry, null if it's unparsable - */ - private static function pxe2BootEntry($section) - { - if (preg_match('/(pxechain\.com|pxechn\.c32)$/i', $section->kernel)) { - // Chaining -- create script - $args = preg_split('/\s+/', $section->append); - $script = ''; - $file = false; - for ($i = 0; $i < count($args); ++$i) { - $arg = $args[$i]; - if ($arg === '-c') { // PXELINUX config file option - ++$i; - $script .= "set 209:string {$args[$i]} || goto %fail%\n"; - } elseif ($arg === '-p') { // PXELINUX prefix path option - ++$i; - $script .= "set 210:string {$args[$i]} || goto %fail%\n"; - } elseif ($arg === '-t') { // PXELINUX timeout option - ++$i; - $script .= "set 211:int32 {$args[$i]} || goto %fail%\n"; - } elseif ($arg === '-o') { // Overriding various DHCP options - ++$i; - if (preg_match('/^((?:0x)?[a-f0-9]{1,4})\.([bwlsh])=(.*)$/i', $args[$i], $out)) { - // TODO: 'q' (8byte) unsupported for now - $opt = intval($out[1], 0); - if ($opt > 0 && $opt < 255) { - static $optType = ['b' => 'uint8', 'w' => 'uint16', 'l' => 'int32', 's' => 'string', 'h' => 'hex']; - $type = $optType[$out[2]]; - $script .= "set {$opt}:{$type} {$args[$i]} || goto %fail%\n"; - } - } - } elseif ($arg{0} === '-') { - continue; - } elseif ($file === false) { - $file = self::parseFile($arg); - } - } - if ($file !== false) { - $url = parse_url($file); - if (isset($url['host'])) { - $script .= "set next-server {$url['host']} || goto %fail%\n"; - } - if (isset($url['path'])) { - $script .= "set filename {$url['path']} || goto %fail%\n"; - } - $script .= "chain -ar {$file} || goto %fail%\n"; - return new CustomBootEntry(['script' => $script]); - } - return null; - } - // "Normal" entry that should be convertible into a StandardBootEntry - $section->kernel = self::parseFile($section->kernel); - foreach ($section->initrd as &$initrd) { - $initrd = self::parseFile($initrd); - } - return BootEntry::newStandardBootEntry($section); - } - - /** - * Parse PXELINUX file notion. Basically, turn - * server::file into tftp://server/file. - * - * @param string $file - * @return string - */ - private static function parseFile($file) - { - if (preg_match(',^([^:/]+)::(.*)$,', $file, $out)) { - return 'tftp://' . $out[1] . '/' . $out[2]; - } - return $file; - } - - public static function sanitizeIpxeString($string) - { - return str_replace(['&', '|', ';', '$', "\r", "\n"], ['+', '/', ':', 'S', ' ', ' '], $string); - } - - public static function makeMd5Pass($plainpass, $salt) - { - if (empty($plainpass)) - return ''; - return md5(md5($plainpass) . '-' . $salt); - } - -} -- cgit v1.2.3-55-g7522