From 2e78eec281815d6ba42ff2cf7c3a937abe6d83c5 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 12 Jun 2018 17:15:44 +0200 Subject: [serversetup-bwlp] Start rewrite as purely iPXE-based --- modules-available/serversetup-bwlp/api.inc.php | 242 +++++++++ modules-available/serversetup-bwlp/config.json | 5 +- .../serversetup-bwlp/inc/bootentry.inc.php | 195 +++++++ .../serversetup-bwlp/inc/ipxe.inc.php | 560 ++++++++++++++------- .../serversetup-bwlp/inc/ipxemenu.inc.php | 142 ++++++ .../serversetup-bwlp/inc/menuentry.inc.php | 170 +++++++ .../serversetup-bwlp/inc/pxelinux.inc.php | 262 ++++++++++ modules-available/serversetup-bwlp/install.inc.php | 74 +++ modules-available/serversetup-bwlp/page.inc.php | 300 ++++++++++- .../serversetup-bwlp/permissions/permissions.json | 14 +- .../serversetup-bwlp/templates/download.html | 38 ++ .../serversetup-bwlp/templates/heading.html | 4 +- .../templates/ipxe-new-boot-entry.html | 108 ++++ .../serversetup-bwlp/templates/ipxe.html | 117 ----- .../serversetup-bwlp/templates/menu-edit.html | 114 +++++ .../serversetup-bwlp/templates/menu-list.html | 36 ++ 16 files changed, 2049 insertions(+), 332 deletions(-) create mode 100644 modules-available/serversetup-bwlp/api.inc.php create mode 100644 modules-available/serversetup-bwlp/inc/bootentry.inc.php create mode 100644 modules-available/serversetup-bwlp/inc/ipxemenu.inc.php create mode 100644 modules-available/serversetup-bwlp/inc/menuentry.inc.php create mode 100644 modules-available/serversetup-bwlp/inc/pxelinux.inc.php create mode 100644 modules-available/serversetup-bwlp/install.inc.php create mode 100644 modules-available/serversetup-bwlp/templates/download.html create mode 100644 modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html delete mode 100644 modules-available/serversetup-bwlp/templates/ipxe.html create mode 100644 modules-available/serversetup-bwlp/templates/menu-edit.html create mode 100644 modules-available/serversetup-bwlp/templates/menu-list.html (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/api.inc.php b/modules-available/serversetup-bwlp/api.inc.php new file mode 100644 index 00000000..36f9063c --- /dev/null +++ b/modules-available/serversetup-bwlp/api.inc.php @@ -0,0 +1,242 @@ + 'exit 1', + 'COMBOOT' => 'chain /tftp/chain.c32 hd0', + 'SANBOOT' => 'sanboot --no-describe', +]; + +$serverIp = Property::getServerIp(); + +$ip = $_SERVER['REMOTE_ADDR']; +if (substr($ip, 0, 7) === '::ffff:') { + $ip = substr($ip, 7); +} +$uuid = Request::any('uuid', false, 'string'); +$menu = IPxeMenu::forClient($ip, $uuid); + +// Get platform - EFI or PCBIOS +$platform = strtoupper(Request::any('platform', 'PCBIOS', 'string')); + +// Get preferred localboot method, depending on system model +$localboot = false; +$model = false; +if ($uuid !== false && Module::get('statistics') !== false) { + $row = Database::queryFirst('SELECT systemmodel FROM machine WHERE machineuuid = :uuid', ['uuid' => $uuid]); + if ($row !== false && !empty($row['systemmodel'])) { + $model = $row['systemmodel']; + } +} +if ($model === false) { + function modfilt($str) + { + if (empty($str) || preg_match('/product\s+name|be\s+filled|unknown|default\s+string/i', $str)) + return false; + return trim(preg_replace('/\s+/', ' ', $str)); + } + $manuf = modfilt(Request::any('manuf', false, 'string')); + $product = modfilt(Request::any('product', false, 'string')); + if (!empty($product)) { + $model = $product; + if (!empty($manuf)) { + $model .= " ($manuf)"; + } + } +} +// Query +if ($model !== false) { + $row = Database::queryFirst("SELECT bootmethod FROM serversetup_localboot WHERE systemmodel = :model LIMIT 1", + ['model' => $model]); + if ($row !== false) { + $localboot = $row['bootmethod']; + } +} +if ($localboot === false || !isset($BOOT_METHODS[$localboot])) { + $localboot = Property::get('serversetup.localboot', false); + if ($localboot === false) { + if ($platform === 'EFI') { + // It seems most (all) EFI platforms won't enumerate any drives in ipxe. + // No idea if this can be fixed in ipxe code in the future. + $localboot = 'EXIT'; + } else { + $localboot = 'SANBOOT'; + } + } +} +if (isset($BOOT_METHODS[$localboot])) { + // Move preferred method first + $BOOT_METHODS[] = $BOOT_METHODS[$localboot]; + unset($BOOT_METHODS[$localboot]); + $BOOT_METHODS = array_reverse($BOOT_METHODS); +} + +$output = <<getMenuDefinition('target'); + +$output .= <<getItemsCode(); + +/* +:i1 +#console || +echo Welcome to Shell || +shell +goto slx_menu + +:i2 +imgfree || +kernel /boot/default/kernel slxbase=boot/default slxsrv=$serverIp splash BOOTIF=01-\${net\${nic}/mac:hexhyp} || echo Could not download kernel +initrd /boot/default/initramfs-stage31 || echo Could not download initrd +boot -ar || goto fail + +:i3 +chain -ar \${self} || +chain -ar /tftp/undionly.kpxe || goto fail + +:i4 +imgfree || +sanboot --no-describe --drive 0x80 || goto fail + +:i5 +chain -a /tftp/memtest.0 passes=1 onepass || goto membad +prompt Memory OK. Press a key. +goto init + +:i6 +console --left 60 --top 130 --right 67 --bottom 96 --quick --picture bg-load --keep || +echo Welcome to Shell || +shell +goto slx_menu + +:i7 +chain -ar tftp://132.230.4.6/ipxelinux.0 || prompt FAILED PRESS A KEY +goto slx_menu + +:i8 +set x:int32 0 +:again +console --left 60 --top 130 --right 67 --bottom 96 --picture bg-load --keep --quick || +console --left 55 --top 88 --right 63 --bottom 64 --picture bg-menu --keep --quick || +inc x +iseq \${x} 20 || goto again +prompt DONE. Press dein Knie. +goto slx_menu + +:i9 +reboot || +prompt Reboot failed. Press a key. +goto slx_menu + +:i10 +poweroff || +prompt Poweroff failed. Press a key. +goto slx_menu + +:membad +iseq \${errno} 0x1 || goto memaborted +params +param scrot \${vram} +imgfetch -a http://132.230.8.113/screen.php##params || +prompt Memory is bad. Press a key. +goto init + +:memaborted +params +param scrot \${vram} +imgfetch -a http://132.230.8.113/screen.php##params || +prompt Memory test aborted. Press a key. +goto init + +*/ + +$output .= << $value) { + if (property_exists($this, $key)) { + $this->{$key} = $value; + } + } + } + } + + public abstract function toScript($failLabel); + + public abstract function toArray(); + + public abstract function addFormFields(&$array); + + /* + * + */ + + /** + * Return a BootEntry instance from the serialized data. + * + * @param string $jsonString serialized entry data + * @return BootEntry|null instance representing boot entry, null on error + */ + public static function fromJson($data) + { + if (is_string($data)) { + $data = json_decode($data, true); + } + if (isset($data['script'])) { + return new CustomBootEntry($data); + } + if (isset($data['executable'])) { + return new StandardBootEntry($data); + } + return null; + } + + public static function newStandardBootEntry($initData) + { + if (empty($initData['executable'])) + return null; + return new StandardBootEntry($initData); + } + + public static function newCustomBootEntry($initData) + { + if (empty($initData['script'])) + return null; + return new CustomBootEntry($initData); + } + + /** + * 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 + */ + public static function fromDatabaseId($id) + { + $row = Database::queryFirst("SELECT data FROM serversetup_bootentry + WHERE entryid = :id LIMIT 1", ['id' => $id]); + if ($row === false) + return false; + return self::fromJson($row['data']); + } + +} + +class StandardBootEntry extends BootEntry +{ + protected $executable; + protected $initRd; + protected $commandLine; + protected $replace; + protected $autoUnload; + protected $resetConsole; + + public function __construct($data = false) + { + if ($data instanceof PxeSection) { + $this->executable = $data->kernel; + $this->initRd = $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'; + } + if ($data->ipAppend & 1) { + $this->commandLine .= ' ${ipappend1}'; + } + if ($data->ipAppend & 2) { + $this->commandLine .= ' ${ipappend2}'; + } + if ($data->ipAppend & 4) { + $this->commandLine .= ' SYSUUID=${uuid}'; + } + $this->commandLine = trim(preg_replace('/\s+/', ' ', $this->commandLine)); + } else { + parent::__construct($data); + } + } + + public function toScript($failLabel) + { + $script = ''; + if ($this->resetConsole) { + $script .= "console ||\n"; + } + if (!empty($this->initRd)) { + $script .= "imgfree ||\n"; + if (!is_array($this->initRd)) { + $script .= "initrd {$this->initRd} || goto $failLabel\n"; + } else { + foreach ($this->initRd as $initrd) { + $script .= "initrd $initrd || goto $failLabel\n"; + } + } + } + $script .= "boot "; + if ($this->autoUnload) { + $script .= "-a "; + } + if ($this->replace) { + $script .= "-r "; + } + $script .= "{$this->executable}"; + if (!empty($this->commandLine)) { + $script .= " {$this->commandLine}"; + } + $script .= " || goto $failLabel\n"; + if ($this->resetConsole) { + $script .= "goto start ||\n"; + } + return $script; + } + + public function addFormFields(&$array) + { + $array['entry'] = [ + 'executable' => $this->executable, + 'initRd' => $this->initRd, + 'commandLine' => $this->commandLine, + 'replace_checked' => $this->replace ? 'checked' : '', + 'autoUnload_checked' => $this->autoUnload ? 'checked' : '', + 'resetConsole_checked' => $this->resetConsole ? 'checked' : '', + ]; + $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, + ]; + } +} + +class CustomBootEntry extends BootEntry +{ + protected $script; + + public function toScript($failLabel) + { + return str_replace('%fail%', $failLabel, $this->script) . "\n"; + } + + public function addFormFields(&$array) + { + $array['entry'] = [ + 'script' => $this->script, + ]; + $array['script_checked'] = 'checked'; + } + + public function toArray() + { + return ['script' => $this->script]; + } +} diff --git a/modules-available/serversetup-bwlp/inc/ipxe.inc.php b/modules-available/serversetup-bwlp/inc/ipxe.inc.php index c42de80b..d5bbb4b2 100644 --- a/modules-available/serversetup-bwlp/inc/ipxe.inc.php +++ b/modules-available/serversetup-bwlp/inc/ipxe.inc.php @@ -1,224 +1,398 @@ ['string', 'title'], - 'menu default' => ['true', 'isDefault'], - 'menu hide' => ['true', 'isHidden'], - 'menu disabled' => ['true', 'isDisabled'], - 'menu indent' => ['int', 'indent'], - 'kernel' => ['string', 'kernel'], - 'initrd' => ['string', 'initrd'], - 'append' => ['string', 'append'], - 'ipappend' => ['int', 'ipAppend'], - 'localboot' => ['int', 'localBoot'], - ]; - $globalPropMap = [ - 'timeout' => ['int', 'timeoutMs', 100], - 'totaltimeout' => ['int', 'totalTimeoutMs', 100], - 'menu title' => ['string', 'title'], - 'menu clear' => ['true', 'menuClear'], - 'menu immediate' => ['true', 'immediateHotkeys'], - 'ontimeout' => ['string', 'timeoutLabel'], - ]; - $lines = preg_split('/[\r\n]+/', $input); - $section = null; - $count = count($lines); - for ($li = 0; $li < $count; ++$li) { - $line =& $lines[$li]; - if (!preg_match('/^\s*([^m]\S*|menu\s+\S+)(\s+.*?|)\s*$/i', $line, $out)) + foreach (glob($configPath . '/*', GLOB_NOSORT) as $file) { + if (!is_file($file) || !preg_match('~/[A-F0-9]{1,8}$~', $file)) continue; - $key = trim($out[1]); - $key = strtolower($key); - $key = preg_replace('/\s+/', ' ', $key); - if ($key === 'label') { - if ($section !== null) { - $menu->sections[] = $section; - } - $section = new PxeSection($out[2]); - } elseif ($key === 'menu separator') { - if ($section !== null) { - $menu->sections[] = $section; - $section = null; - } - $menu->sections[] = new PxeSection(null); - } elseif (self::handleKeyword($key, $out[2], $globalPropMap, $menu)) { + $content = file_get_contents($file); + if ($content === false) continue; - } elseif ($section === null) { - continue; - } elseif ($key === 'text' && strtolower($out[2]) === 'help') { - $text = ''; - while (++$li < $count) { - $line =& $lines[$li]; - if (strtolower(trim($line)) === 'endtext') - break; - $text .= $line . "\n"; + $file = basename($file); + $start = hexdec(str_pad($file,8, '0')); + $end = hexdec(str_pad($file,8, 'f')); // TODO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^PREFIX + error_log('From ' . long2ip($start) . ' to ' . long2ip($end)); + $res = Database::simpleQuery("SELECT locationid, startaddr, endaddr FROM subnet + WHERE startaddr >= :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; + } } - $section->helpText = $text; - } elseif (self::handleKeyword($key, $out[2], $sectionPropMap, $section)) { + unset($loc); + $locations[] = $row; + } + $menuId = self::insertMenu($content, 'Imported', false, 0, [], []); + if ($menuId === false) continue; + foreach ($locations as $loc) { + Database::exec('INSERT IGNORE INTO serversetup_menu_x_location (menuid, locationid) + VALUES (:menuid, :locationid)', ['menuid' => $menuId, 'locationid' => $loc['locationid']]); } } - if ($section !== null) { - $menu->sections[] = $section; + } + + 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']; + } } - return $menu; + $append = [ + '', + 'bwlp-default-dbg' => false, + '', + 'poweroff' => false, + ]; + return self::insertMenu($pxeConfig, $menuTitle, $defaultLabel, $timeoutSecs, $prepend, $append); } - /** - * Check if keyword is valid and if so, add its interpreted value - * to the given object. The map to look up the keyword has to be passed - * as well as the object to set the value in. Map and object should - * obviously match. - * @param string $key keyword of parsed line - * @param string $val raw value of currently parsed line (empty if not present) - * @param array $map Map in which $key is looked up as key - * @param PxeMenu|PxeSection The object to set the parsed and sanitized value in - * @return bool true if the value was found in the map (and set in the object), false otherwise - */ - private static function handleKeyword($key, $val, $map, $object) + private static function insertMenu($pxeConfig, $menuTitle, $defaultLabel, $defaultTimeoutSeconds, $prepend, $append) { - if (!isset($map[$key])) + $timeoutMs = []; + $menuEntries = $prepend; + settype($menuEntries, 'array'); + if (!empty($pxeConfig)) { + $pxe = PxeLinux::parsePxeLinux($pxeConfig); + 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; + } + $section->mangle(); + 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; - $opt = $map[$key]; - // opt[0] is the type the value should be cast to; special case "true" means - // this is a bool option that will be set as soon as the keyword is present, - // as it doesn't have any parameters - if ($opt[0] === 'true') { - $val = true; + // Make menu + $timeoutMs = array_filter($timeoutMs, 'is_int'); + if (empty($timeoutMs)) { + $timeoutMs = (int)($defaultTimeoutSeconds * 1000); } else { - settype($val, $opt[0]); + $timeoutMs = min($timeoutMs); } - // If opt[2] is present it's a multiplier for the value - if (isset($opt[2])) { - $val *= $opt[2]; + $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]; } - $object->{$opt[1]} = $val; - return true; + // 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 ${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 ${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% ||', + ]), + ]); + } -/** - * Class representing a parsed pxelinux menu. Members - * will be set to their annotated type if present or - * be null otherwise, except for present-only boolean - * options, which will default to false. - */ -class PxeMenu -{ - /** - * @var string menu title, shown at the top of the menu - */ - public $title; - /** - * @var int initial timeout after which $timeoutLabel would be executed - */ - public $timeoutMs; - /** - * @var int if the user canceled the timeout by pressing a key, this timeout would still eventually - * trigger and launch the $timeoutLabel section - */ - public $totalTimeoutMs; - /** - * @var string label of section which will execute if the timeout expires - */ - public $timeoutLabel; - /** - * @var bool hide menu and just show background after triggering an entry - */ - public $menuClear = false; /** - * @var bool boot the associated entry directly if its corresponding hotkey is presed instead of just highlighting + * 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 */ - public $immediateHotkeys = false; - /** - * @var PxeSection[] list of sections the menu contains - */ - public $sections = []; -} + 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); + } -/** - * Class representing a parsed pxelinux menu entry. Members - * will be set to their annotated type if present or - * be null otherwise, except for present-only boolean - * options, which will default to false. - */ -class PxeSection -{ - /** - * @var string label used internally in PXEMENU definition to address this entry - */ - public $label; - /** - * @var string MENU LABEL of PXEMENU - title of entry displayed to the user - */ - public $title; - /** - * @var int Number of spaces to prefix the title with - */ - public $indent; - /** - * @var string help text to display when the entry is highlighted - */ - public $helpText; - /** - * @var string Kernel to load - */ - public $kernel; - /** - * @var string initrd to load for the kernel - */ - public $initrd; - /** - * @var string command line options to pass to the kernel - */ - public $append; - /** - * @var int IPAPPEND from PXEMENU. Bitmask of valid options 1 and 2. - */ - public $ipAppend; - /** - * @var string Password protecting the entry. This is most likely in crypted form. - */ - public $passwd; - /** - * @var bool whether this section is marked as default (booted after timeout) - */ - public $isDefault = false; - /** - * @var bool Menu entry is not visible (can only be triggered by timeout) - */ - public $isHidden = false; /** - * @var bool Disable this entry, making it unselectable + * @param PxeSection $section + * @return BootEntry|null The according boot entry, null if it's unparsable */ - public $isDisabled = false; + 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); + } + /** - * @var int Value of the LOCALBOOT field + * Parse PXELINUX file notion. Basically, turn + * server::file into tftp://server/file. + * + * @param string $file + * @return string */ - public $localBoot; + private static function parseFile($file) + { + if (preg_match(',^([^:/]+)::(.*)$,', $file, $out)) { + return 'tftp://' . $out[1] . '/' . $out[2]; + } + return $file; + } - public function __construct($label) { $this->label = $label; } -} + 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); + } +} diff --git a/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php b/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php new file mode 100644 index 00000000..ed9f0986 --- /dev/null +++ b/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php @@ -0,0 +1,142 @@ + $menu]); + if (!is_array($menu)) { + $menu = ['menuid' => 'foo', 'title' => 'Invalid Menu ID: ' . (int)$menu]; + } + } + $this->menuid = (int)$menu['menuid']; + $this->timeoutMs = (int)$menu['timeoutms']; + $this->title = $menu['title']; + $this->defaultEntryId = $menu['defaultentryid']; + $res = Database::simpleQuery("SELECT e.menuentryid, e.entryid, e.hotkey, e.title, e.hidden, e.sortval, e.md5pass, + b.data AS bootentry + FROM serversetup_menuentry e + LEFT JOIN serversetup_bootentry b USING (entryid) + WHERE e.menuid = :menuid + ORDER BY e.sortval ASC, e.title ASC", ['menuid' => $menu['menuid']]); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $this->items[] = new MenuEntry($row); + } + } + + public function getMenuDefinition($targetVar) + { + $str = "menu {$this->title}\n"; + foreach ($this->items as $item) { + $str .= $item->getMenuItemScript("m_{$this->menuid}", $this->defaultEntryId); + } + if ($this->defaultEntryId === null) { + $defaultLabel = "mx_{$this->menuid}_poweroff"; + } else { + $defaultLabel = "m_{$this->menuid}_{$this->defaultEntryId}"; + } + $str .= "choose"; + if ($this->timeoutMs > 0) { + $str .= " --timeout {$this->timeoutMs}"; + } + $str .= " $targetVar || goto $defaultLabel || goto fail\n"; + if ($this->defaultEntryId === null) { + $str .= "goto skip_{$defaultLabel}\n" + . ":{$defaultLabel}\n" + . "poweroff || goto fail\n" + . ":skip_{$defaultLabel}\n"; + } + return $str; + } + + public function getItemsCode() + { + $str = ''; + foreach ($this->items as $item) { + $str .= $item->getBootEntryScript("m_{$this->menuid}", 'fail'); + $str .= "goto slx_menu\n"; + } + return $str; + } + + /* + * + */ + + public static function forLocation($locationId) + { + $chain = null; + if (Module::isAvailable('location')) { + $chain = Location::getLocationRootChain($locationId); + } + if (!empty($chain)) { + $res = Database::simpleQuery("SELECT m.menuid, m.timeoutms, m.title, m.defaultentryid, ml.locationid + FROM serversetup_menu m + INNER JOIN serversetup_menu_location ml USING (menuid) + WHERE ml.locationid IN (:chain)", ['chain' => $chain]); + if ($res->rowCount() > 0) { + // Make the location id key, preserving order (closest location is first) + $chain = array_flip($chain); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + // Overwrite the value (numeric ascending values, useless) with menu array of according location + $chain[(int)$row['locationid']] = $row; + } + // Use first one that was found + foreach ($chain as $menu) { + if (is_array($menu)) { + return new IPxeMenu($menu); + } + } + // Should never end up here, but we'd just fall through and use the default + } + } + // We're here, no specific menu, use default + $menu = Database::queryFirst("SELECT menuid, timeoutms, title, defaultentryid + FROM serversetup_menu + ORDER BY isdefault DESC LIMIT 1"); + if ($menu === false) { + return new EmptyIPxeMenu; + } + return new IPxeMenu($menu); + } + + public static function forClient($ip, $uuid) + { + $locationId = 0; + if (Module::isAvailable('location')) { + $locationId = Location::getFromIpAndUuid($ip, $uuid); + } + return self::forLocation($locationId); + } + +} + +class EmptyIPxeMenu extends IPxeMenu +{ + + /** @noinspection PhpMissingParentConstructorInspection */ + public function __construct() + { + $this->title = 'No menu defined'; + $this->menuid = -1; + $this->items[] = new MenuEntry([ + 'title' => 'Please create a menu in Server-Setup first' + ]); + $this->items[] = new MenuEntry([ + 'title' => 'Bitte erstellen Sie zunächst ein Menü' + ]); + } + +} \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/inc/menuentry.inc.php b/modules-available/serversetup-bwlp/inc/menuentry.inc.php new file mode 100644 index 00000000..9736b7bb --- /dev/null +++ b/modules-available/serversetup-bwlp/inc/menuentry.inc.php @@ -0,0 +1,170 @@ + $value) { + if (property_exists($this, $key)) { + $this->{$key} = $value; + } + } + $this->hotkey = self::getKeyCode($row['hotkey']); + if (!empty($row['bootentry'])) { + $this->bootEntry = BootEntry::fromJson($row['bootentry']); + } + $this->gap = (array_key_exists('entryid', $row) && $row['entryid'] === null); + } + settype($this->hidden, 'bool'); + settype($this->gap, 'bool'); + settype($this->sortval, 'int'); + settype($this->menuentryid, 'int'); + } + + public function getMenuItemScript($lblPrefix, $requestedDefaultId) + { + $str = 'item '; + if ($this->gap) { + $str .= '--gap '; + } else { + if ($this->hidden) { + if ($this->hotkey === false) + return ''; // Hidden entries without hotkey are illegal + $str .= '--hidden '; + } + if ($this->hotkey !== false) { + $str .= '--key ' . $this->hotkey . ' '; + } + if ($this->menuentryid == $requestedDefaultId) { + $str .= '--default '; + } + $str .= "{$lblPrefix}_{$this->menuentryid} "; + } + $str .= $this->title; + return $str . " || prompt Could not create menu item for {$lblPrefix}_{$this->menuentryid}\n"; + } + + public function getBootEntryScript($lblPrefix, $failLabel) + { + if ($this->bootEntry === null) + return ''; + $str = ":{$lblPrefix}_{$this->menuentryid}\n"; + if (!empty($this->md5pass)) { + $str .= "set slx_hash {$this->md5pass} || goto $failLabel\n" + . "set slx_salt {$this->menuentryid} || goto $failLabel\n" + . "set slx_pw_ok {$lblPrefix}_ok || goto $failLabel\n" + . "set slx_pw_fail slx_menu || goto $failLabel\n" + . "goto slx_pass_check || goto $failLabel\n" + . ":{$lblPrefix}_ok\n"; + } + return $str . $this->bootEntry->toScript($failLabel); + } + + /* + * + */ + + private static function getKeyArray() + { + static $data = false; + if ($data === false) { + $data = [ + 'F5' => 0x107e, + 'F6' => 0x127e, + 'F7' => 0x137e, + 'F8' => 0x147e, + 'F9' => 0x157e, + 'F10' => 0x167e, + 'F11' => 0x187e, + 'F12' => 0x197e, + ]; + for ($i = 1; $i <= 26; ++$i) { + $letter = chr(0x40 + $i); + $data['SHIFT_' . $letter] = 0x40 + $i; + if ($letter !== 'C') { + $data['CTRL_' . $letter] = $i; + } + $data[$letter] = 0x60 + $i; + } + for ($i = 0; $i <= 9; ++$i) { + $data[chr(0x30 + $i)] = 0x30 + $i; + } + asort($data, SORT_NUMERIC); + } + return $data; + } + + /** + * Get all the known/supported keys, usable for menu items. + * + * @return string[] list of known key names + */ + public static function getKeyList() + { + return array_keys(self::getKeyArray()); + } + + /** + * Get the key code ipxe expects for the given named + * key. Returns false if the key name is unknown. + * + * @param string $keyName + * @return false|string Key code as hex string, or false if not found + */ + public static function getKeyCode($keyName) + { + $data = self::getKeyArray(); + if (isset($data[$keyName])) + return '0x' . dechex($data[$keyName]); + return false; + } + + /** + * @param string $keyName desired key name + * @return string $keyName if it's known, empty string otherwise + */ + public static function filterKeyName($keyName) + { + if (isset($data[$keyName])) + return $keyName; + return ''; + } + +} diff --git a/modules-available/serversetup-bwlp/inc/pxelinux.inc.php b/modules-available/serversetup-bwlp/inc/pxelinux.inc.php new file mode 100644 index 00000000..db3dac4b --- /dev/null +++ b/modules-available/serversetup-bwlp/inc/pxelinux.inc.php @@ -0,0 +1,262 @@ + ['string', 'title'], + 'menu default' => ['true', 'isDefault'], + 'menu hide' => ['true', 'isHidden'], + 'menu disabled' => ['true', 'isDisabled'], + 'menu indent' => ['int', 'indent'], + 'kernel' => ['string', 'kernel'], + 'com32' => ['string', 'kernel'], + 'pxe' => ['string', 'kernel'], + 'initrd' => ['string', 'initrd'], + 'append' => ['string', 'append'], + 'ipappend' => ['int', 'ipAppend'], + 'sysappend' => ['int', 'ipAppend'], + 'localboot' => ['int', 'localBoot'], + ]; + $globalPropMap = [ + 'timeout' => ['int', 'timeoutMs', 100], + 'totaltimeout' => ['int', 'totalTimeoutMs', 100], + 'menu title' => ['string', 'title'], + 'menu clear' => ['true', 'menuClear'], + 'menu immediate' => ['true', 'immediateHotkeys'], + 'ontimeout' => ['string', 'timeoutLabel'], + ]; + $lines = preg_split('/[\r\n]+/', $input); + $section = null; + $count = count($lines); + for ($li = 0; $li < $count; ++$li) { + $line =& $lines[$li]; + if (!preg_match('/^\s*([^m]\S*|menu\s+\S+)(\s+.*?|)\s*$/i', $line, $out)) + continue; + $val = trim($out[2]); + $key = trim($out[1]); + $key = strtolower($key); + $key = preg_replace('/\s+/', ' ', $key); + if ($key === 'label') { + if ($section !== null) { + $menu->sections[] = $section; + } + $section = new PxeSection($val); + } elseif ($key === 'menu separator') { + if ($section !== null) { + $menu->sections[] = $section; + $section = null; + } + $menu->sections[] = new PxeSection(null); + } elseif (self::handleKeyword($key, $val, $globalPropMap, $menu)) { + continue; + } elseif ($section === null) { + continue; + } elseif ($key === 'text' && strtolower($val) === 'help') { + $text = ''; + while (++$li < $count) { + $line =& $lines[$li]; + if (strtolower(trim($line)) === 'endtext') + break; + $text .= $line . "\n"; + } + $section->helpText = $text; + } elseif (self::handleKeyword($key, $val, $sectionPropMap, $section)) { + continue; + } + } + if ($section !== null) { + $menu->sections[] = $section; + } + return $menu; + } + + /** + * Check if keyword is valid and if so, add its interpreted value + * to the given object. The map to look up the keyword has to be passed + * as well as the object to set the value in. Map and object should + * obviously match. + * @param string $key keyword of parsed line + * @param string $val raw value of currently parsed line (empty if not present) + * @param array $map Map in which $key is looked up as key + * @param PxeMenu|PxeSection The object to set the parsed and sanitized value in + * @return bool true if the value was found in the map (and set in the object), false otherwise + */ + private static function handleKeyword($key, $val, $map, $object) + { + if (!isset($map[$key])) + return false; + $opt = $map[$key]; + // opt[0] is the type the value should be cast to; special case "true" means + // this is a bool option that will be set as soon as the keyword is present, + // as it doesn't have any parameters + if ($opt[0] === 'true') { + $val = true; + } else { + settype($val, $opt[0]); + } + // If opt[2] is present it's a multiplier for the value + if (isset($opt[2])) { + $val *= $opt[2]; + } + $object->{$opt[1]} = $val; + return true; + } + +} + +/** + * Class representing a parsed pxelinux menu. Members + * will be set to their annotated type if present or + * be null otherwise, except for present-only boolean + * options, which will default to false. + */ +class PxeMenu +{ + /** + * @var string menu title, shown at the top of the menu + */ + public $title; + /** + * @var int initial timeout after which $timeoutLabel would be executed + */ + public $timeoutMs; + /** + * @var int if the user canceled the timeout by pressing a key, this timeout would still eventually + * trigger and launch the $timeoutLabel section + */ + public $totalTimeoutMs; + /** + * @var string label of section which will execute if the timeout expires + */ + public $timeoutLabel; + /** + * @var bool hide menu and just show background after triggering an entry + */ + public $menuClear = false; + /** + * @var bool boot the associated entry directly if its corresponding hotkey is pressed instead of just highlighting + */ + public $immediateHotkeys = false; + /** + * @var PxeSection[] list of sections the menu contains + */ + public $sections = []; +} + +/** + * Class representing a parsed pxelinux menu entry. Members + * will be set to their annotated type if present or + * be null otherwise, except for present-only boolean + * options, which will default to false. + */ +class PxeSection +{ + /** + * @var string label used internally in PXEMENU definition to address this entry + */ + public $label; + /** + * @var string MENU LABEL of PXEMENU - title of entry displayed to the user + */ + public $title; + /** + * @var int Number of spaces to prefix the title with + */ + public $indent; + /** + * @var string help text to display when the entry is highlighted + */ + public $helpText; + /** + * @var string Kernel to load + */ + public $kernel; + /** + * @var string|string[] initrd to load for the kernel. + * If mangle() has been called this will be an array, + * otherwise it's a comma separated list. + */ + public $initrd; + /** + * @var string command line options to pass to the kernel + */ + public $append; + /** + * @var int IPAPPEND from PXEMENU. Bitmask of valid options 1 and 2. + */ + public $ipAppend; + /** + * @var string Password protecting the entry. This is most likely in crypted form. + */ + public $passwd; + /** + * @var bool whether this section is marked as default (booted after timeout) + */ + public $isDefault = false; + /** + * @var bool Menu entry is not visible (can only be triggered by timeout) + */ + public $isHidden = false; + /** + * @var bool Disable this entry, making it unselectable + */ + public $isDisabled = false; + /** + * @var int Value of the LOCALBOOT field + */ + public $localBoot; + /** + * @var string hotkey to trigger item. Only valid after calling mangle() + */ + public $hotkey; + + public function __construct($label) { $this->label = $label; } + + public function mangle() + { + if (($i = strpos($this->title, '^')) !== false) { + $this->hotkey = strtoupper($this->title{$i+1}); + $this->title = substr($this->title, 0, $i) . substr($this->title, $i + 1); + } + if (strpos($this->append, 'initrd=') !== false) { + $parts = preg_split('/\s+/', $this->append); + $this->append = ''; + for ($i = 0; $i < count($parts); ++$i) { + if (preg_match('/^initrd=(.*)$/', $parts[$i], $out)) { + if (!empty($this->initrd)) { + $this->initrd .= ','; + } + $this->initrd .= $out[1]; + } else { + $this->append .= ' ' . $parts[$i]; + } + } + $this->append = trim($this->append); + } + if (is_string($this->initrd)) { + $this->initrd = explode(',', $this->initrd); + } elseif (!is_array($this->initrd)) { + $this->initrd = []; + } + } +} + diff --git a/modules-available/serversetup-bwlp/install.inc.php b/modules-available/serversetup-bwlp/install.inc.php new file mode 100644 index 00000000..8814bb7c --- /dev/null +++ b/modules-available/serversetup-bwlp/install.inc.php @@ -0,0 +1,74 @@ +handleGetImage(); @@ -44,16 +50,45 @@ class Page_ServerSetup extends Page $this->updatePxeMenu(); } + if ($action === 'savebootentry') { + User::assertPermission('ipxe.bootentry.edit'); + $this->saveBootEntry(); + } + + if ($action === 'savemenu') { + User::assertPermission('ipxe.menu.edit'); + $this->saveMenu(); + } + if (Request::isPost()) { Util::redirect('?do=serversetup'); } User::assertPermission('access-page'); + + if (User::hasPermission('ipxe.*')) { + Dashboard::addSubmenu('?do=serversetup&show=menu', Dictionary::translate('submenu_menu', true)); + } + if (User::hasPermission('edit.address')) { + Dashboard::addSubmenu('?do=serversetup&show=address', Dictionary::translate('submenu_address', true)); + } + if (User::hasPermission('download')) { + Dashboard::addSubmenu('?do=serversetup&show=download', Dictionary::translate('submenu_download', true)); + } + if (Request::get('show') === false) { + $subs = Dashboard::getSubmenus(); + if (empty($subs)) { + User::assertPermission('download'); + } else { + Util::redirect($subs[0]['url']); + } + } } protected function doRender() { Render::addTemplate("heading"); + $task = Property::get('ipxe-task-id'); if ($task !== false) { $task = Taskmanager::status($task); @@ -65,32 +100,143 @@ class Page_ServerSetup extends Page Render::addTemplate('ipxe_update', array('taskid' => $task['id'])); } - Permission::addGlobalTags($perms, null, ['edit.menu', 'edit.address', 'download']); + switch (Request::get('show')) { + case 'editbootentry': + User::assertPermission('ipxe.bootentry.edit'); + $this->showEditBootEntry(); + break; + case 'editmenu': + User::assertPermission('ipxe.menu.view'); + $this->showEditMenu(); + break; + case 'download': + User::assertPermission('download'); + $this->showDownload(); + break; + case 'menu': + User::assertPermission('ipxe.menu.view'); + $this->showMenuList(); + break; + default: + Util::redirect('?do=serversetup'); + break; + } + } + + private function showDownload() + { + // TODO: Make nicer, support more variants (taskmanager-plugin) + Render::addTemplate('download'); + } + + private function showMenuList() + { + $allowedEdit = User::getAllowedLocations('ipxe.menu.edit'); + + // TODO Permission::addGlobalTags($perms, null, ['edit.menu', 'edit.address', 'download']); + + $res = Database::simpleQuery("SELECT m.menuid, m.title, m.isdefault, GROUP_CONCAT(l.locationid) AS locations + FROM serversetup_menu m LEFT JOIN serversetup_menu_location l USING (menuid) GROUP BY menuid ORDER BY title"); + $table = []; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if (empty($row['locations'])) { + $locations = []; + $row['allowEdit'] = in_array(0, $allowedEdit); + } else { + $locations = explode(',', $row['locations']); + $row['allowEdit'] = empty(array_diff($locations, $allowedEdit)); + } + $row['locationCount'] = empty($locations) ? '' : count($locations); + $table[] = $row; + } - Render::addTemplate('ipaddress', array( - 'ips' => $this->taskStatus['data']['addresses'], - 'chooseHintClass' => $this->hasIpSet ? '' : 'alert alert-danger', - 'editAllowed' => User::hasPermission("edit.address"), - 'perms' => $perms, + Render::addTemplate('menu-list', array( + 'table' => $table, )); - $data = $this->currentMenu; - if (!User::hasPermission('edit.menu')) { - unset($data['masterpasswordclear']); + } + + private function hasMenuPermission($menuid, $permission) + { + $allowedEditLocations = User::getAllowedLocations($permission); + $allowEdit = in_array(0, $allowedEditLocations); + if (!$allowEdit) { + // Get locations + $locations = Database::queryColumnArray('SELECT locationid FROM serversetup_menu_location + WHERE menuid = :menuid', compact('menuid')); + if (!empty($locations)) { + $allowEdit = count(array_diff($locations, $allowedEditLocations)) === 0; + } } - if (!isset($data['defaultentry'])) { - $data['defaultentry'] = 'net'; + return $allowEdit; + } + + private function showEditMenu() + { + $id = Request::get('id', false, 'int'); + $menu = Database::queryFirst("SELECT menuid, timeoutms, title, defaultentryid, isdefault + FROM serversetup_menu WHERE menuid = :id", compact('id')); + if ($menu === false) { + Message::addError('invalid-menu-id', $id); + Util::redirect('?do=serversetup&show=menu'); } - if ($data['defaultentry'] === 'net') { - $data['active-net'] = 'checked'; + if (!$this->hasMenuPermission($id, 'ipxe.menu.edit')) { + $menu['readonly'] = 'readonly'; + $menu['disabled'] = 'disabled'; + $menu['plainpass'] = ''; } - if ($data['defaultentry'] === 'hdd') { - $data['active-hdd'] = 'checked'; + $menu['timeout'] = round($menu['timeoutms'] / 1000); + $menu['entries'] = Database::queryAll("SELECT menuentryid, entryid, hotkey, title, hidden, sortval, plainpass FROM + serversetup_menuentry WHERE menuid = :id", compact('id')); + $keyList = array_map(function ($item) { return ['key' => $item]; }, MenuEntry::getKeyList()); + $entryList = Database::queryAll("SELECT entryid, title, hotkey FROM serversetup_bootentry ORDER BY title ASC"); + foreach ($menu['entries'] as &$entry) { + $entry['isdefault'] = ($entry['menuentryid'] == $menu['defaultentryid']); + $entry['keys'] = $keyList; + foreach ($entry['keys'] as &$key) { + if ($key['key'] === $entry['hotkey']) { + $key['selected'] = 'selected'; // TODO: plainpass only when permissions + } + } + $entry['entrylist'] = $entryList; + foreach ($entry['entrylist'] as &$item) { + if ($item['entryid'] == $entry['entryid']) { + $item['selected'] = 'selected'; + } + if (empty($item['title'])) { + $item['title'] = $item['entryid']; + } + } } - if ($data['defaultentry'] === 'custom') { - $data['active-custom'] = 'checked'; + // TODO: Make assigned locations editable + Permission::addGlobalTags($menu['perms'], 0, ['ipxe.menu.edit']); + Render::addTemplate('menu-edit', $menu); + } + + private function showEditBootEntry() + { + $params = []; + $id = Request::get('id', false, 'string'); + if ($id === false) { + $params['exec_checked'] = 'checked'; + $params['entryid'] = 'u-' . dechex(mt_rand(0x1000, 0xffff)) . '-' . dechex(time()); + } else { + // Query existing entry + $row = Database::queryFirst('SELECT entryid, title, builtin, data FROM serversetup_bootentry + WHERE entryid = :id LIMIT 1', ['id' => $id]); + if ($row === false) { + Message::addError('invalid-boot-entry', $id); + Util::redirect('?do=serversetup'); + } + $entry = BootEntry::fromJson($row['data']); + if ($entry === null) { + Message::addError('unknown-boot-entry-type', $id); + Util::redirect('?do=serversetup'); + } + $entry->addFormFields($params); + $params['title'] = $row['title']; + $params['oldentryid'] = $params['entryid'] = $row['entryid']; } - $data['perms'] = $perms; - Render::addTemplate('ipxe', $data); + Render::addTemplate('ipxe-new-boot-entry', $params); } // ----------------------------------------------------------------------------------------------- @@ -131,6 +277,76 @@ class Page_ServerSetup extends Page return true; } + private function saveMenu() + { + $id = Request::post('menuid', false, 'int'); + if ($id === false) { + Message::addError('main.parameter-missing', 'menuid'); + return; + } + $menu = Database::queryFirst("SELECT m.menuid, GROUP_CONCAT(l.locationid) AS locations + FROM serversetup_menu m + LEFT JOIN serversetup_menu_location l USING (menuid) + WHERE menuid = :id", compact('id')); + if ($menu === false) { + Message::addError('no-such-menu', $id); + return; + } + if (!$this->hasMenuPermission($id, 'ipxe.menu.edit')) { + Message::addError('locations.no-permission-location', 'TODO'); + return; + } + // TODO: Validate new locations to be saved (and actually save them) + + Database::exec('UPDATE serversetup_menu SET title = :title, timeoutms = :timeoutms + WHERE menuid = :menuid', [ + 'menuid' => $id, + 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), + 'timeoutms' => abs(Request::post('timeoutms', 0, 'int') * 1000), + ]); + if (User::hasPermission('ipxe.menu.edit', 0)) { + Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); + } + + $keepIds = []; + $entries = Request::post('entry', false, 'array'); + foreach ($entries as $key => $entry) { + $params = [ + 'entryid' => $entry['entryid'], // TODO validate + 'hotkey' => MenuEntry::filterKeyName($entry['hotkey']), + 'title' => IPxe::sanitizeIpxeString($entry['title']), + 'hidden' => (int)$entry['hidden'], + 'sortval' => (int)$entry['sortval'], + 'plainpass' => $entry['plainpass'], + 'menuid' => $menu['menuid'], + ]; + if (is_numeric($key)) { + $keepIds[] = $key; + $params['menuentryid'] = $key; + $params['md5pass'] = IPxe::makeMd5Pass($entry['plainpass'], $key); + $ret = Database::exec('UPDATE serversetup_menuentry + SET entryid = :entryid, hotkey = :hotkey, title = :title, hidden = :hidden, sortval = :sortval, + plainpass = :plainpass, md5pass = :md5pass + WHERE menuid = :menuid AND menuentryid = :menuentryid', $params, true); + } else { + $ret = Database::exec("INSERT INTO serversetup_menuentry + (menuid, entryid, hotkey, title, hidden, sortval, plainpass, md5pass) + VALUES (:menuid, :entryid, :hotkey, :title, :hidden, :sortval, :plainpass, '')", $params, true); + if ($ret && !empty($entry['plainpass'])) { + $key = Database::lastInsertId(); + Database::exec('UPDATE serversetup_menuentry SET md5pass = :md5pass WHERE menuentryid = :id', [ + 'md5pass' => IPxe::makeMd5Pass($entry['plainpass'], $key), + 'key' => $id, + ]); + } + } + if ($ret === false) { + Message::addWarning('error-saving-entry', $entry['title'], Database::lastError()); + } + } + Message::addSuccess('menu-saved'); + } + private function updateLocalAddress() { $newAddress = Request::post('ip', 'none'); @@ -184,4 +400,50 @@ class Page_ServerSetup extends Page exit; } + private function saveBootEntry() + { + $oldEntryId = Request::post('entryid', false, 'string'); + $newId = Request::post('newid', false, 'string'); + if (!preg_match('/^[a-z0-9\-_]{1,16}$/', $newId)) { + Message::addError('main.parameter-empty', 'newid'); + return; + } + $data = Request::post('entry', false); + if (!is_array($data)) { + Message::addError('missing-entry-data'); + return; + } + $type = Request::post('type', false, 'string'); + if ($type === 'exec') { + $entry = BootEntry::newStandardBootEntry($data); + } elseif ($type === 'script') { + $entry = BootEntry::newCustomBootEntry($data); + } else { + Message::addError('unknown-entry-type', $type); + return; + } + if ($entry === null) { + Message::addError('main.empty-field'); + return; + } + $params = [ + 'entryid' => $newId, + 'title' => Request::post('title', '', 'string'), + 'data' => json_encode($entry->toArray()), + ]; + // New or update? + if (empty($oldEntryId)) { + // New entry + Database::exec('INSERT INTO serversetup_bootentry (entryid, title, builtin, data) + VALUES (:entryid, :title, 0, :data)', $params); + Message::addSuccess('boot-entry-created', $newId); + } else { + // Edit existing entry + $params['oldid'] = $oldEntryId; + Database::exec('UPDATE serversetup_bootentry SET entryid = :entryid, title = :title, data = :data + WHERE entryid = :oldid AND builtin = 0', $params); + Message::addSuccess('boot-entry-updated', $newId); + } + } + } diff --git a/modules-available/serversetup-bwlp/permissions/permissions.json b/modules-available/serversetup-bwlp/permissions/permissions.json index 44927506..aa2aa001 100644 --- a/modules-available/serversetup-bwlp/permissions/permissions.json +++ b/modules-available/serversetup-bwlp/permissions/permissions.json @@ -8,7 +8,19 @@ "edit.address": { "location-aware": false }, - "edit.menu": { + "ipxe.bootentry.view": { + "location-aware": false + }, + "ipxe.bootentry.edit": { + "location-aware": false + }, + "ipxe.menu.view": { + "location-aware": false + }, + "ipxe.menu.edit": { + "location-aware": true + }, + "ipxe.localboot.edit": { "location-aware": false } } \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/templates/download.html b/modules-available/serversetup-bwlp/templates/download.html new file mode 100644 index 00000000..6752f7fc --- /dev/null +++ b/modules-available/serversetup-bwlp/templates/download.html @@ -0,0 +1,38 @@ + + + \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/templates/heading.html b/modules-available/serversetup-bwlp/templates/heading.html index d68360f1..e2aa0bff 100644 --- a/modules-available/serversetup-bwlp/templates/heading.html +++ b/modules-available/serversetup-bwlp/templates/heading.html @@ -1 +1,3 @@ -

{{lang_moduleHeading}}

\ No newline at end of file + \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html b/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html new file mode 100644 index 00000000..fd9e1d72 --- /dev/null +++ b/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html @@ -0,0 +1,108 @@ +

{{lang_newBootEntryHead}}

+ +
+
+ {{lang_bootEntryData}} +
+
+
+ + + + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+ + +
+
+ +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/templates/ipxe.html b/modules-available/serversetup-bwlp/templates/ipxe.html deleted file mode 100644 index f4b0b4d3..00000000 --- a/modules-available/serversetup-bwlp/templates/ipxe.html +++ /dev/null @@ -1,117 +0,0 @@ -
- - - - -
-
- {{lang_bootMenu}} -
-
-

- {{lang_bootInfo}} -

-
- -
- {{lang_bootBehavior}} -
- - -
-
- - -
-
- - -
-
- -
- {{lang_menuDisplayTime}} -
- - {{lang_seconds}} -
-
- -
- {{lang_masterPassword}} -
- -
- {{lang_masterPasswordHelp}} -
- -
- {{lang_menuCustom}} - -
-
- - -
-
- - - - \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/templates/menu-edit.html b/modules-available/serversetup-bwlp/templates/menu-edit.html new file mode 100644 index 00000000..cf10296e --- /dev/null +++ b/modules-available/serversetup-bwlp/templates/menu-edit.html @@ -0,0 +1,114 @@ +

{{lang_editMenuHead}}

+ +
+
+ {{title}} + {{^title}} + {{lang_newMenu}} + {{/title}} +
+
+
+ + + +
+
+ +
+
+ +
+
+
+
+ +
+
+
+ + {{lang_seconds}} +
+
+
+
+
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + {{#entries}} + + + + + {{#entryid}} + + {{/entryid}} + + + + {{/entries}} + +
{{lang_entryId}}{{lang_title}}{{lang_hotkey}}{{lang_sortOrder}}{{lang_password}}
+
+ + +
+
+ {{#entryid}} + + {{/entryid}} + {{^entryid}} + {{lang_spacer}} + {{/entryid}} + + + + + + + + +
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/modules-available/serversetup-bwlp/templates/menu-list.html b/modules-available/serversetup-bwlp/templates/menu-list.html new file mode 100644 index 00000000..a862cff2 --- /dev/null +++ b/modules-available/serversetup-bwlp/templates/menu-list.html @@ -0,0 +1,36 @@ +

{{lang_listOfMenus}}

+ + + + + + + + + + + + {{#table}} + + + + + + + {{/table}} + +
{{lang_menuTitle}}{{lang_locationCount}}{{lang_isDefault}}{{lang_edit}}
+ {{title}} + + {{locationCount}} + + {{#isdefault}} + + {{/isdefault}} + + {{#allowEdit}} + + + + {{/allowEdit}} +
\ No newline at end of file -- cgit v1.2.3-55-g7522 From 35578a5c327baa803e600700ccaee627d4724339 Mon Sep 17 00:00:00 2001 From: Jannik Schönartz Date: Wed, 20 Jun 2018 20:02:18 +0200 Subject: [serversetup-bwlp] Added drag and drop to the editmenu. Fixed radiobutton alignment and it's now saved/updated properly. --- modules-available/serversetup-bwlp/config.json | 3 +- modules-available/serversetup-bwlp/page.inc.php | 12 +++++- .../serversetup-bwlp/templates/menu-edit.html | 44 +++++++++++++++++----- 3 files changed, 48 insertions(+), 11 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/config.json b/modules-available/serversetup-bwlp/config.json index 8ce65a85..ff485760 100644 --- a/modules-available/serversetup-bwlp/config.json +++ b/modules-available/serversetup-bwlp/config.json @@ -1,6 +1,7 @@ { "category": "main.settings-server", "dependencies" : [ - "locations" + "locations", + "js_jqueryui" ] } \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 4207bd53..f2c422f3 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -189,6 +189,7 @@ class Page_ServerSetup extends Page serversetup_menuentry WHERE menuid = :id", compact('id')); $keyList = array_map(function ($item) { return ['key' => $item]; }, MenuEntry::getKeyList()); $entryList = Database::queryAll("SELECT entryid, title, hotkey FROM serversetup_bootentry ORDER BY title ASC"); + $sortVals = array(); foreach ($menu['entries'] as &$entry) { $entry['isdefault'] = ($entry['menuentryid'] == $menu['defaultentryid']); $entry['keys'] = $keyList; @@ -206,7 +207,12 @@ class Page_ServerSetup extends Page $item['title'] = $item['entryid']; } } + $sortVals[] = $entry['sortval']; } + $arr = $menu['entries']; + $keys = array_keys($arr); + array_multisort( $sortVals, SORT_ASC, $arr, $keys); + $menu['entries'] = $arr; // TODO: Make assigned locations editable Permission::addGlobalTags($menu['perms'], 0, ['ipxe.menu.edit']); Render::addTemplate('menu-edit', $menu); @@ -298,11 +304,12 @@ class Page_ServerSetup extends Page } // TODO: Validate new locations to be saved (and actually save them) - Database::exec('UPDATE serversetup_menu SET title = :title, timeoutms = :timeoutms + Database::exec('UPDATE serversetup_menu SET title = :title, timeoutms = :timeoutms, defaultentryid = :defaultentryid WHERE menuid = :menuid', [ 'menuid' => $id, 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), 'timeoutms' => abs(Request::post('timeoutms', 0, 'int') * 1000), + 'defaultentryid' => Request::post('defaultentry', false, 'int'), ]); if (User::hasPermission('ipxe.menu.edit', 0)) { Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); @@ -310,6 +317,7 @@ class Page_ServerSetup extends Page $keepIds = []; $entries = Request::post('entry', false, 'array'); + foreach ($entries as $key => $entry) { $params = [ 'entryid' => $entry['entryid'], // TODO validate @@ -340,9 +348,11 @@ class Page_ServerSetup extends Page ]); } } + if ($ret === false) { Message::addWarning('error-saving-entry', $entry['title'], Database::lastError()); } + } Message::addSuccess('menu-saved'); } diff --git a/modules-available/serversetup-bwlp/templates/menu-edit.html b/modules-available/serversetup-bwlp/templates/menu-edit.html index cf10296e..aa748c3a 100644 --- a/modules-available/serversetup-bwlp/templates/menu-edit.html +++ b/modules-available/serversetup-bwlp/templates/menu-edit.html @@ -12,6 +12,7 @@ +
@@ -46,24 +47,30 @@ + - - + {{#entries}} - - + + + + + {{/entryid}} - + - + {{#entries}} - + @@ -120,13 +120,13 @@ \ No newline at end of file -- cgit v1.2.3-55-g7522 From 1229906bc79254bc22c9e08e04a025d8bb1d89bf Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 4 Jul 2018 13:02:31 +0200 Subject: [serversetup-bwlp] Delete menu entries missing from POST data --- modules-available/serversetup-bwlp/page.inc.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 00b658c4..dc53070b 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -343,20 +343,24 @@ class Page_ServerSetup extends Page $ret = Database::exec("INSERT INTO serversetup_menuentry (menuid, entryid, hotkey, title, hidden, sortval, plainpass, md5pass) VALUES (:menuid, :entryid, :hotkey, :title, :hidden, :sortval, :plainpass, '')", $params, true); - if ($ret && !empty($entry['plainpass'])) { + if ($ret) { $key = Database::lastInsertId(); - Database::exec('UPDATE serversetup_menuentry SET md5pass = :md5pass WHERE menuentryid = :id', [ - 'md5pass' => IPxe::makeMd5Pass($entry['plainpass'], $key), - 'key' => $id, - ]); + $keepIds[] = (int)$key; + if (!empty($entry['plainpass'])) { + Database::exec('UPDATE serversetup_menuentry SET md5pass = :md5pass WHERE menuentryid = :id', [ + 'md5pass' => IPxe::makeMd5Pass($entry['plainpass'], $key), + 'id' => $key, + ]); + } } } if ($ret === false) { Message::addWarning('error-saving-entry', $entry['title'], Database::lastError()); } - } + Database::exec('DELETE FROM serversetup_menuentry WHERE menuid = :menuid AND menuentryid NOT IN (:keep)', + ['menuid' => $menu['menuid'], 'keep' => $keepIds]); Message::addSuccess('menu-saved'); } -- cgit v1.2.3-55-g7522 From 43c02c0eb26a5253663db244346ec1f2a5b62eb2 Mon Sep 17 00:00:00 2001 From: Christian Hofmaier Date: Tue, 24 Jul 2018 12:41:51 +0200 Subject: [ipxe] added add-menu-button, added delete-menu-buttons, fixed bug that timeoutms didn't save, fixed bug that last edited menu gets the isDefault value, some table styling + language tags --- .../serversetup-bwlp/lang/de/template-tags.json | 6 ++ .../serversetup-bwlp/lang/en/template-tags.json | 6 ++ modules-available/serversetup-bwlp/page.inc.php | 75 ++++++++++++++++++---- .../serversetup-bwlp/permissions/permissions.json | 6 ++ .../serversetup-bwlp/templates/menu-list.html | 54 ++++++++++++++-- 5 files changed, 131 insertions(+), 16 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/lang/de/template-tags.json b/modules-available/serversetup-bwlp/lang/de/template-tags.json index 8d612ab0..14f1c134 100644 --- a/modules-available/serversetup-bwlp/lang/de/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/de/template-tags.json @@ -1,5 +1,6 @@ { "lang_active": "Aktiv", + "lang_addMenu": "Menü hinzufügen", "lang_bootAddress": "Boot-Adresse des Servers", "lang_bootBehavior": "Standard-Bootverhalten", "lang_bootHint": "Das Bootmen\u00fc muss nach einer \u00c4nderung der IP-Adresse neu generiert werden. In der Regel geschieht dies automatisch, der Vorgang kann in der Sektion Bootmen\u00fc allerdings auch manuell ausgel\u00f6st werden.", @@ -8,11 +9,15 @@ "lang_bootMenuCreate": "Bootmen\u00fc erzeugen", "lang_chooseIP": "Bitte w\u00e4hlen Sie die IP-Adresse, \u00fcber die der Server von den Clients zum Booten angesprochen werden soll.", "lang_customEntry": "Eigener Eintrag", + "lang_menuDeleteConfirm": "Sind Sie sicher, dass Sie dieses Menü löschen wollen?", "lang_downloadImage": "USB-Image herunterladen", "lang_downloadRufus": "Rufus herunterladen", "lang_example": "Beispiel", "lang_generationFailed": "Erzeugen des Bootmen\u00fcs fehlgeschlagen. Der Netzwerkboot von bwLehrpool wird wahrscheinlich nicht funktionieren. Wenn Sie den Fehler nicht selbst beheben k\u00f6nnen, melden Sie bitte die Logausgabe an das bwLehrpool-Projekt.", + "lang_isDefault": "Standard", + "lang_listOfMenus": "Menüliste", "lang_localHDD": "Lokale HDD", + "lang_locationCount": "Anzahl Orte", "lang_masterPassword": "Master-Passwort", "lang_masterPasswordHelp": "Das Master-Passwort wird ben\u00f6tigt, um einen Booteintrag direkt am Client tempor\u00e4r durch Dr\u00fccken der Tab-Taste zu editieren. Da dies f\u00fcr Manipulation am Client genutzt werden kann, sollte diese Funktion unbedingt mit einem Passwort gesch\u00fctzt werden.", "lang_menuCustom": "Benutzerdefinierter Men\u00fczusatz", @@ -21,6 +26,7 @@ "lang_menuCustomHint3": "und w\u00e4hlen Sie als Standard-Bootverhalten ebenfalls custom.", "lang_menuDisplayTime": "Anzeigedauer des Men\u00fcs", "lang_menuGeneration": "Erzeugen des Bootmen\u00fcs", + "lang_menuTitle": "Menü", "lang_moduleHeading": "iPXE \/ Boot Menu", "lang_pxeBuilt": "PXE-Binary gebaut", "lang_seconds": "Sekunden", diff --git a/modules-available/serversetup-bwlp/lang/en/template-tags.json b/modules-available/serversetup-bwlp/lang/en/template-tags.json index 9bb55f93..d70159e2 100644 --- a/modules-available/serversetup-bwlp/lang/en/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/en/template-tags.json @@ -1,5 +1,6 @@ { "lang_active": "Active", + "lang_addMenu": "Add Menu", "lang_bootAddress": "Boot Address of the Server", "lang_bootBehavior": "Default Boot Behavior", "lang_bootHint": "The Boot menu must be recreated after changing the IP address. Usually this is done automatically, but the process can also be triggered manually in the section of the boot menu.", @@ -8,11 +9,15 @@ "lang_bootMenuCreate": "Create Boot Menu", "lang_chooseIP": "Please select the IP address that the client server will use to boot.", "lang_customEntry": "Custom entry", + "lang_menuDeleteConfirm": "Are you sure you want to delete this menu?", "lang_downloadImage": "Download USB Image", "lang_downloadRufus": "Download Rufus", "lang_example": "Example", "lang_generationFailed": "Could not generate boot menu. The bwLehrpool-System might not work properly. If you can't fix the problem, please report the error log below to the bwLehrpool project.", + "lang_isDefault": "Default", + "lang_listOfMenus": "Menulist", "lang_localHDD": "Local HDD", + "lang_locationCount": "Number of Locations", "lang_masterPassword": "Master Password", "lang_masterPasswordHelp": "The master password is required to edit a boot menu entry. This should be set for security reasons.", "lang_menuCustom": "Custom Extra Menu", @@ -21,6 +26,7 @@ "lang_menuCustomHint3": "and select as the default boot behavior custom as well.", "lang_menuDisplayTime": "Menu Display Time", "lang_menuGeneration": "Generating boot menu...", + "lang_menuTitle": "Menu", "lang_moduleHeading": "iPXE \/ Boot Menu", "lang_pxeBuilt": "Built PXE binary", "lang_seconds": "Seconds", diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index dc53070b..6df4c49c 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -60,6 +60,11 @@ class Page_ServerSetup extends Page $this->saveMenu(); } + if ($action === 'deleteMenu') { + User::assertPermission('ipxe.menu.delete'); + $this->deleteMenu(); + } + if (Request::isPost()) { Util::redirect('?do=serversetup'); } @@ -132,26 +137,35 @@ class Page_ServerSetup extends Page private function showMenuList() { $allowedEdit = User::getAllowedLocations('ipxe.menu.edit'); + $allowedDelete = User::getAllowedLocations('ipxe.menu.delete'); // TODO Permission::addGlobalTags($perms, null, ['edit.menu', 'edit.address', 'download']); $res = Database::simpleQuery("SELECT m.menuid, m.title, m.isdefault, GROUP_CONCAT(l.locationid) AS locations FROM serversetup_menu m LEFT JOIN serversetup_menu_location l USING (menuid) GROUP BY menuid ORDER BY title"); - $table = []; + $menuTable = []; while ($row = $res->fetch(PDO::FETCH_ASSOC)) { if (empty($row['locations'])) { $locations = []; $row['allowEdit'] = in_array(0, $allowedEdit); + $row['allowDelete'] = in_array(0, $allowedDelete); } else { $locations = explode(',', $row['locations']); $row['allowEdit'] = empty(array_diff($locations, $allowedEdit)); + $row['allowDelete'] = empty(array_diff($locations, $allowedDelete)); } $row['locationCount'] = empty($locations) ? '' : count($locations); - $table[] = $row; + $menuTable[] = $row; + } + + $allowAddMenu = 'disabled'; + if (User::hasPermission('ipxe.menu.add')) { + $allowAddMenu = ''; } Render::addTemplate('menu-list', array( - 'table' => $table, + 'menuTable' => $menuTable, + 'allowAddMenu' => $allowAddMenu )); } @@ -173,8 +187,19 @@ class Page_ServerSetup extends Page private function showEditMenu() { $id = Request::get('id', false, 'int'); - $menu = Database::queryFirst("SELECT menuid, timeoutms, title, defaultentryid, isdefault + // if = edit, else = add new + if ($id != 0) { + $menu = Database::queryFirst("SELECT menuid, timeoutms, title, defaultentryid, isdefault FROM serversetup_menu WHERE menuid = :id", compact('id')); + } else { + $menu = []; + $menu['menuid'] = 0; + $menu['timeoutms'] = 0; + $menu['title'] = ''; + $menu['defaultentryid'] = null; + $menu['isdefault'] = false; + } + if ($menu === false) { Message::addError('invalid-menu-id', $id); Util::redirect('?do=serversetup&show=menu'); @@ -263,6 +288,22 @@ class Page_ServerSetup extends Page return true; } + private function deleteMenu() + { + $id = Request::post('deleteid', false, 'int'); + if ($id === false) { + Message::addError('main.parameter-missing', 'menuid'); + return; + } + if (!$this->hasMenuPermission($id, 'ipxe.menu.delete')) { + Message::addError('locations.no-permission-location', 'TODO'); + return; + } + Database::exec("DELETE FROM serversetup_menu WHERE menuid = :menuid", array("menuid" => $id)); + Message::addSuccess('menu-deleted'); + + } + private function saveMenu() { $id = Request::post('menuid', false, 'int'); @@ -284,15 +325,27 @@ class Page_ServerSetup extends Page } // TODO: Validate new locations to be saved (and actually save them) - Database::exec('UPDATE serversetup_menu SET title = :title, timeoutms = :timeoutms, defaultentryid = :defaultentryid + if ($id == 0) { + Database::exec("INSERT IGNORE INTO serversetup_menu (title, timeoutms, defaultentryid) VALUES (:title, :timeoutms, :defaultentryid)", [ + 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), + 'timeoutms' => abs(Request::post('timeout', 0, 'int') * 1000), + 'defaultentryid' => Request::post('defaultentry', null, 'int') + ]); + } else { + Database::exec('UPDATE serversetup_menu SET title = :title, timeoutms = :timeoutms, defaultentryid = :defaultentryid WHERE menuid = :menuid', [ - 'menuid' => $id, - 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), - 'timeoutms' => abs(Request::post('timeoutms', 0, 'int') * 1000), - 'defaultentryid' => Request::post('defaultentry', null, 'int'), - ]); + 'menuid' => $id, + 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), + 'timeoutms' => abs(Request::post('timeout', 0, 'int') * 1000), + 'defaultentryid' => Request::post('defaultentry', null, 'int'), + ]); + } + + $defmenu = Request::post('defmenu', false, 'boolean'); if (User::hasPermission('ipxe.menu.edit', 0)) { - Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); + if ($defmenu) { + Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); + } } $keepIds = []; diff --git a/modules-available/serversetup-bwlp/permissions/permissions.json b/modules-available/serversetup-bwlp/permissions/permissions.json index aa2aa001..e05b9f6c 100644 --- a/modules-available/serversetup-bwlp/permissions/permissions.json +++ b/modules-available/serversetup-bwlp/permissions/permissions.json @@ -20,6 +20,12 @@ "ipxe.menu.edit": { "location-aware": true }, + "ipxe.menu.add": { + "location-aware": false + }, + "ipxe.menu.delete": { + "location-aware": true + }, "ipxe.localboot.edit": { "location-aware": false } diff --git a/modules-available/serversetup-bwlp/templates/menu-list.html b/modules-available/serversetup-bwlp/templates/menu-list.html index a862cff2..1f190bb7 100644 --- a/modules-available/serversetup-bwlp/templates/menu-list.html +++ b/modules-available/serversetup-bwlp/templates/menu-list.html @@ -7,10 +7,11 @@ + - {{#table}} + {{#menuTable}} - - + - {{/table}} + {{/menuTable}} -
{{lang_entryId}} {{lang_title}} {{lang_hotkey}}{{lang_sortOrder}} {{lang_password}}
-
+
+ + +
{{#entryid}} - - @@ -111,4 +116,25 @@ - \ No newline at end of file + + + \ No newline at end of file -- cgit v1.2.3-55-g7522 From 303e32fe9fbbe0897ea53dd8dd41c60abf1f0434 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 28 Jun 2018 12:35:30 +0200 Subject: [serversetup-bwlp] Simplify sorting of menu entries --- modules-available/serversetup-bwlp/page.inc.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index f2c422f3..061abfd5 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -186,10 +186,9 @@ class Page_ServerSetup extends Page } $menu['timeout'] = round($menu['timeoutms'] / 1000); $menu['entries'] = Database::queryAll("SELECT menuentryid, entryid, hotkey, title, hidden, sortval, plainpass FROM - serversetup_menuentry WHERE menuid = :id", compact('id')); + serversetup_menuentry WHERE menuid = :id ORDER BY sortval ASC", compact('id')); $keyList = array_map(function ($item) { return ['key' => $item]; }, MenuEntry::getKeyList()); $entryList = Database::queryAll("SELECT entryid, title, hotkey FROM serversetup_bootentry ORDER BY title ASC"); - $sortVals = array(); foreach ($menu['entries'] as &$entry) { $entry['isdefault'] = ($entry['menuentryid'] == $menu['defaultentryid']); $entry['keys'] = $keyList; @@ -207,12 +206,7 @@ class Page_ServerSetup extends Page $item['title'] = $item['entryid']; } } - $sortVals[] = $entry['sortval']; } - $arr = $menu['entries']; - $keys = array_keys($arr); - array_multisort( $sortVals, SORT_ASC, $arr, $keys); - $menu['entries'] = $arr; // TODO: Make assigned locations editable Permission::addGlobalTags($menu['perms'], 0, ['ipxe.menu.edit']); Render::addTemplate('menu-edit', $menu); -- cgit v1.2.3-55-g7522 From e778ad169141aadb3372f5f1778e735c0aa99676 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 28 Jun 2018 12:37:10 +0200 Subject: [serversetup-bwlp] Slightly nicer colors; html cleanup --- modules-available/serversetup-bwlp/templates/menu-edit.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/templates/menu-edit.html b/modules-available/serversetup-bwlp/templates/menu-edit.html index aa748c3a..4fabd11c 100644 --- a/modules-available/serversetup-bwlp/templates/menu-edit.html +++ b/modules-available/serversetup-bwlp/templates/menu-edit.html @@ -55,10 +55,10 @@ {{lang_password}}
{{lang_locationCount}} {{lang_isDefault}} {{lang_edit}}{{lang_delete}}
{{title}} @@ -18,19 +19,62 @@ {{locationCount}} + {{#isdefault}} {{/isdefault}} + {{#allowEdit}} {{/allowEdit}} + {{#allowDelete}} + + {{/allowDelete}} +
\ No newline at end of file + + + + + +
+ + +
+ + \ No newline at end of file -- cgit v1.2.3-55-g7522 From 60b1fc7329725612fb5e6289295c5efd0f31f36d Mon Sep 17 00:00:00 2001 From: Christian Hofmaier Date: Tue, 24 Jul 2018 15:36:48 +0200 Subject: [ipxe] Add Bootentry list Bootentry list with add/edit/delete functionality --- .../serversetup-bwlp/lang/de/template-tags.json | 3 + .../serversetup-bwlp/lang/en/template-tags.json | 3 + modules-available/serversetup-bwlp/page.inc.php | 47 +++++++++++++- .../serversetup-bwlp/permissions/permissions.json | 6 ++ .../serversetup-bwlp/templates/bootentry-list.html | 71 ++++++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 modules-available/serversetup-bwlp/templates/bootentry-list.html (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/lang/de/template-tags.json b/modules-available/serversetup-bwlp/lang/de/template-tags.json index 14f1c134..bda890c1 100644 --- a/modules-available/serversetup-bwlp/lang/de/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/de/template-tags.json @@ -1,14 +1,17 @@ { "lang_active": "Aktiv", + "lang_addBootentry": "Booteintrag hinzufügen", "lang_addMenu": "Menü hinzufügen", "lang_bootAddress": "Boot-Adresse des Servers", "lang_bootBehavior": "Standard-Bootverhalten", + "lang_bootentryTitle": "Booteintrag", "lang_bootHint": "Das Bootmen\u00fc muss nach einer \u00c4nderung der IP-Adresse neu generiert werden. In der Regel geschieht dies automatisch, der Vorgang kann in der Sektion Bootmen\u00fc allerdings auch manuell ausgel\u00f6st werden.", "lang_bootInfo": "Hier k\u00f6nnen Anpassungen am Erscheinungsbild des Bootmen\u00fcs vorgenommen werden.", "lang_bootMenu": "Bootmen\u00fc", "lang_bootMenuCreate": "Bootmen\u00fc erzeugen", "lang_chooseIP": "Bitte w\u00e4hlen Sie die IP-Adresse, \u00fcber die der Server von den Clients zum Booten angesprochen werden soll.", "lang_customEntry": "Eigener Eintrag", + "lang_bootentryDeleteConfirm": "Sind Sie sicher, dass Sie diesen Booteintrag löschen wollen?", "lang_menuDeleteConfirm": "Sind Sie sicher, dass Sie dieses Menü löschen wollen?", "lang_downloadImage": "USB-Image herunterladen", "lang_downloadRufus": "Rufus herunterladen", diff --git a/modules-available/serversetup-bwlp/lang/en/template-tags.json b/modules-available/serversetup-bwlp/lang/en/template-tags.json index d70159e2..121ed3e7 100644 --- a/modules-available/serversetup-bwlp/lang/en/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/en/template-tags.json @@ -1,14 +1,17 @@ { "lang_active": "Active", + "lang_addBootentry": "Add Bootentry", "lang_addMenu": "Add Menu", "lang_bootAddress": "Boot Address of the Server", "lang_bootBehavior": "Default Boot Behavior", + "lang_bootentryTitle": "Bootentry", "lang_bootHint": "The Boot menu must be recreated after changing the IP address. Usually this is done automatically, but the process can also be triggered manually in the section of the boot menu.", "lang_bootInfo": "Here adjustments can be made to the appearance of the boot menu.", "lang_bootMenu": "Boot Menu", "lang_bootMenuCreate": "Create Boot Menu", "lang_chooseIP": "Please select the IP address that the client server will use to boot.", "lang_customEntry": "Custom entry", + "lang_bootentryDeleteConfirm": "Are you sure you want to delete this bootentry?", "lang_menuDeleteConfirm": "Are you sure you want to delete this menu?", "lang_downloadImage": "Download USB Image", "lang_downloadRufus": "Download Rufus", diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 6df4c49c..3ef4371f 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -55,6 +55,11 @@ class Page_ServerSetup extends Page $this->saveBootEntry(); } + if ($action === 'deleteBootentry') { + User::assertPermission('ipxe.bootentry.delete'); + $this->deleteBootEntry(); + } + if ($action === 'savemenu') { User::assertPermission('ipxe.menu.edit'); $this->saveMenu(); @@ -73,6 +78,7 @@ class Page_ServerSetup extends Page if (User::hasPermission('ipxe.*')) { Dashboard::addSubmenu('?do=serversetup&show=menu', Dictionary::translate('submenu_menu', true)); + Dashboard::addSubmenu('?do=serversetup&show=bootentry', Dictionary::translate('submenu_bootentry', true)); } if (User::hasPermission('edit.address')) { Dashboard::addSubmenu('?do=serversetup&show=address', Dictionary::translate('submenu_address', true)); @@ -122,6 +128,10 @@ class Page_ServerSetup extends Page User::assertPermission('ipxe.menu.view'); $this->showMenuList(); break; + case 'bootentry': + User::assertPermission('ipxe.bootentry.view'); + $this->showBootentryList(); + break; default: Util::redirect('?do=serversetup'); break; @@ -134,6 +144,29 @@ class Page_ServerSetup extends Page Render::addTemplate('download'); } + private function showBootentryList() + { + $allowEdit = User::hasPermission('ipxe.bootentry.edit'); + $allowDelete = User::hasPermission('ipxe.bootentry.delete'); + $allowAdd = 'disabled'; + if (User::hasPermission('ipxe.bootentry.add')) { + $allowAdd = ''; + } + + $res = Database::simpleQuery("SELECT entryid, hotkey, title FROM serversetup_bootentry"); + $bootentryTable = []; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $bootentryTable[] = $row; + } + + Render::addTemplate('bootentry-list', array( + 'bootentryTable' => $bootentryTable, + 'allowAdd' => $allowAdd, + 'allowEdit' => $allowEdit, + 'allowDelete' => $allowDelete + )); + } + private function showMenuList() { $allowedEdit = User::getAllowedLocations('ipxe.menu.edit'); @@ -288,11 +321,22 @@ class Page_ServerSetup extends Page return true; } + private function deleteBootEntry() { + $id = Request::post('deleteid', false, 'string'); + if ($id === false) { + Message::addError('main.parameter-missing', 'deleteid'); + return; + } + Database::exec("DELETE FROM serversetup_bootentry WHERE entryid = :entryid", array("entryid" => $id)); + // TODO: Redirect to &show=bootentry + Message::addSuccess('bootentry-deleted'); + } + private function deleteMenu() { $id = Request::post('deleteid', false, 'int'); if ($id === false) { - Message::addError('main.parameter-missing', 'menuid'); + Message::addError('main.parameter-missing', 'deleteid'); return; } if (!$this->hasMenuPermission($id, 'ipxe.menu.delete')) { @@ -512,6 +556,7 @@ class Page_ServerSetup extends Page $params['oldid'] = $oldEntryId; Database::exec('UPDATE serversetup_bootentry SET entryid = :entryid, title = :title, data = :data WHERE entryid = :oldid AND builtin = 0', $params); + // TODO: Redirect to &show=bootentry Message::addSuccess('boot-entry-updated', $newId); } } diff --git a/modules-available/serversetup-bwlp/permissions/permissions.json b/modules-available/serversetup-bwlp/permissions/permissions.json index e05b9f6c..5b97e5c2 100644 --- a/modules-available/serversetup-bwlp/permissions/permissions.json +++ b/modules-available/serversetup-bwlp/permissions/permissions.json @@ -14,6 +14,12 @@ "ipxe.bootentry.edit": { "location-aware": false }, + "ipxe.bootentry.add": { + "location-aware": false + }, + "ipxe.bootentry.delete": { + "location-aware": false + }, "ipxe.menu.view": { "location-aware": false }, diff --git a/modules-available/serversetup-bwlp/templates/bootentry-list.html b/modules-available/serversetup-bwlp/templates/bootentry-list.html new file mode 100644 index 00000000..f9e881b2 --- /dev/null +++ b/modules-available/serversetup-bwlp/templates/bootentry-list.html @@ -0,0 +1,71 @@ + + + + + + + + + + + {{#bootentryTable}} + + + + + + + {{/bootentryTable}} + +
{{lang_bootentryTitle}}Hotkey{{lang_edit}}{{lang_delete}}
+ {{title}} + + {{hotkey}} + + {{#allowEdit}} + + + + {{/allowEdit}} + + {{#allowDelete}} + + {{/allowDelete}} +
+ + + +
+ + +
+ + \ No newline at end of file -- cgit v1.2.3-55-g7522 From 870ca20a54a901c56daf691243bd2bb6c5269a26 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 25 Jul 2018 16:10:33 +0200 Subject: [serversetup-bwlp] Fix some menu edit issues - Newly created menu entries couldn't be made default - Saving a new menu was broken --- modules-available/serversetup-bwlp/page.inc.php | 75 ++++++++++++---------- .../serversetup-bwlp/templates/menu-edit.html | 2 +- 2 files changed, 41 insertions(+), 36 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 3ef4371f..25a31f06 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -221,7 +221,7 @@ class Page_ServerSetup extends Page { $id = Request::get('id', false, 'int'); // if = edit, else = add new - if ($id != 0) { + if ($id !== 0) { $menu = Database::queryFirst("SELECT menuid, timeoutms, title, defaultentryid, isdefault FROM serversetup_menu WHERE menuid = :id", compact('id')); } else { @@ -355,45 +355,41 @@ class Page_ServerSetup extends Page Message::addError('main.parameter-missing', 'menuid'); return; } - $menu = Database::queryFirst("SELECT m.menuid, GROUP_CONCAT(l.locationid) AS locations + // TODO: Validate new locations to be saved (and actually save them) + $insertParams = [ + 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), + 'timeoutms' => abs(Request::post('timeout', 0, 'int') * 1000), + ]; + if ($id === 0) { + Database::exec("INSERT INTO serversetup_menu (title, timeoutms) VALUES (:title, :timeoutms)", $insertParams); + $menu['menuid'] = $id = Database::lastInsertId(); + } else { + $menu = Database::queryFirst("SELECT m.menuid, GROUP_CONCAT(l.locationid) AS locations FROM serversetup_menu m LEFT JOIN serversetup_menu_location l USING (menuid) WHERE menuid = :id", compact('id')); - if ($menu === false) { - Message::addError('no-such-menu', $id); - return; - } - if (!$this->hasMenuPermission($id, 'ipxe.menu.edit')) { - Message::addError('locations.no-permission-location', 'TODO'); - return; + if ($menu === false) { + Message::addError('no-such-menu', $id); + return; + } + if (!$this->hasMenuPermission($id, 'ipxe.menu.edit')) { + Message::addError('locations.no-permission-location', 'TODO'); + return; + } + $insertParams['menuid'] = $id; + Database::exec('UPDATE serversetup_menu SET title = :title, timeoutms = :timeoutms + WHERE menuid = :menuid', $insertParams); } - // TODO: Validate new locations to be saved (and actually save them) - if ($id == 0) { - Database::exec("INSERT IGNORE INTO serversetup_menu (title, timeoutms, defaultentryid) VALUES (:title, :timeoutms, :defaultentryid)", [ - 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), - 'timeoutms' => abs(Request::post('timeout', 0, 'int') * 1000), - 'defaultentryid' => Request::post('defaultentry', null, 'int') - ]); - } else { - Database::exec('UPDATE serversetup_menu SET title = :title, timeoutms = :timeoutms, defaultentryid = :defaultentryid - WHERE menuid = :menuid', [ - 'menuid' => $id, - 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), - 'timeoutms' => abs(Request::post('timeout', 0, 'int') * 1000), - 'defaultentryid' => Request::post('defaultentry', null, 'int'), - ]); - } - - $defmenu = Request::post('defmenu', false, 'boolean'); - if (User::hasPermission('ipxe.menu.edit', 0)) { - if ($defmenu) { - Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); - } + if (User::hasPermission('ipxe.menu.edit', 0) + && Request::post('defmenu', false, 'boolean')) { + Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); } $keepIds = []; $entries = Request::post('entry', false, 'array'); + $wantedDefaultEntryId = Request::post('defaultentry', null, 'string'); + $defaultEntryId = null; foreach ($entries as $key => $entry) { if (!isset($entry['sortval'])) { @@ -429,6 +425,9 @@ class Page_ServerSetup extends Page ]; } if (is_numeric($key)) { + if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key + $defaultEntryId = $key; + } $keepIds[] = $key; $params['menuentryid'] = $key; $params['md5pass'] = IPxe::makeMd5Pass($entry['plainpass'], $key); @@ -441,12 +440,15 @@ class Page_ServerSetup extends Page (menuid, entryid, hotkey, title, hidden, sortval, plainpass, md5pass) VALUES (:menuid, :entryid, :hotkey, :title, :hidden, :sortval, :plainpass, '')", $params, true); if ($ret) { - $key = Database::lastInsertId(); - $keepIds[] = (int)$key; + $newKey = Database::lastInsertId(); + if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key + $defaultEntryId = $newKey; + } + $keepIds[] = (int)$newKey; if (!empty($entry['plainpass'])) { Database::exec('UPDATE serversetup_menuentry SET md5pass = :md5pass WHERE menuentryid = :id', [ - 'md5pass' => IPxe::makeMd5Pass($entry['plainpass'], $key), - 'id' => $key, + 'md5pass' => IPxe::makeMd5Pass($entry['plainpass'], $newKey), + 'id' => $newKey, ]); } } @@ -458,6 +460,9 @@ class Page_ServerSetup extends Page } Database::exec('DELETE FROM serversetup_menuentry WHERE menuid = :menuid AND menuentryid NOT IN (:keep)', ['menuid' => $menu['menuid'], 'keep' => $keepIds]); + // Set default entry + Database::exec('UPDATE serversetup_menu SET defaultentryid = :default WHERE menuid = :menuid', + ['menuid' => $menu['menuid'], 'default' => $defaultEntryId]); Message::addSuccess('menu-saved'); } diff --git a/modules-available/serversetup-bwlp/templates/menu-edit.html b/modules-available/serversetup-bwlp/templates/menu-edit.html index 603c7425..c0a353b4 100644 --- a/modules-available/serversetup-bwlp/templates/menu-edit.html +++ b/modules-available/serversetup-bwlp/templates/menu-edit.html @@ -41,7 +41,7 @@
- +
-- cgit v1.2.3-55-g7522 From 4242bf4439ed4c2466e1eac0f0e920d04870e210 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 13 Sep 2018 16:10:09 +0200 Subject: [serversetup-bwlp] Pass initrd= on KCL for EFI mode --- modules-available/serversetup-bwlp/inc/bootentry.inc.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/inc/bootentry.inc.php b/modules-available/serversetup-bwlp/inc/bootentry.inc.php index f488959b..930f4413 100644 --- a/modules-available/serversetup-bwlp/inc/bootentry.inc.php +++ b/modules-available/serversetup-bwlp/inc/bootentry.inc.php @@ -135,8 +135,9 @@ class StandardBootEntry extends BootEntry $script .= "-r "; } $script .= "{$this->executable}"; + $rdBase = basename($this->initRd); if (!empty($this->commandLine)) { - $script .= " {$this->commandLine}"; + $script .= " initrd=$rdBase {$this->commandLine}"; } $script .= " || goto $failLabel\n"; if ($this->resetConsole) { -- cgit v1.2.3-55-g7522 From 6da78493a0bb010a0fff50ac3cc23e018b15e979 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 19 Sep 2018 11:27:22 +0200 Subject: [serversetup-bwlp] Differentiate between EFI/BIOS Different executable/initrd etc. can be given for a simple boot entry of type "exec", or it can be specified that only one of them is supported. For bootentry type "script" there can still be only one entry, since you can just check the ${platform} variable within the script. --- modules-available/serversetup-bwlp/api.inc.php | 12 +- .../serversetup-bwlp/inc/bootentry.inc.php | 113 ++++++++++++++----- .../serversetup-bwlp/inc/ipxemenu.inc.php | 8 +- .../serversetup-bwlp/inc/menuentry.inc.php | 10 +- modules-available/serversetup-bwlp/page.inc.php | 7 +- .../templates/ipxe-new-boot-entry.html | 122 ++++++++++++++------- 6 files changed, 194 insertions(+), 78 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/api.inc.php b/modules-available/serversetup-bwlp/api.inc.php index 36f9063c..52f30440 100644 --- a/modules-available/serversetup-bwlp/api.inc.php +++ b/modules-available/serversetup-bwlp/api.inc.php @@ -1,5 +1,7 @@ 'exit 1', 'COMBOOT' => 'chain /tftp/chain.c32 hd0', @@ -22,12 +24,14 @@ $platform = strtoupper(Request::any('platform', 'PCBIOS', 'string')); $localboot = false; $model = false; if ($uuid !== false && Module::get('statistics') !== false) { + // If we have the machine table, we rather try to look up the system model from there, using the UUID $row = Database::queryFirst('SELECT systemmodel FROM machine WHERE machineuuid = :uuid', ['uuid' => $uuid]); if ($row !== false && !empty($row['systemmodel'])) { $model = $row['systemmodel']; } } if ($model === false) { + // Otherwise use what iPXE sent us function modfilt($str) { if (empty($str) || preg_match('/product\s+name|be\s+filled|unknown|default\s+string/i', $str)) @@ -70,6 +74,8 @@ if (isset($BOOT_METHODS[$localboot])) { $BOOT_METHODS = array_reverse($BOOT_METHODS); } +// TODO: Feature check for our own iPXE extensions, stay compatible to stock iPXE + $output = <<getMenuDefinition('target'); +$output .= $menu->getMenuDefinition('target', $platform); $output .= <<getItemsCode(); +$output .= $menu->getItemsCode($platform); + +// TODO: Work out memtest stuff. Needs to be put on server (install/update script) -- PCBIOS only? Chain EFI -> BIOS? /* :i1 diff --git a/modules-available/serversetup-bwlp/inc/bootentry.inc.php b/modules-available/serversetup-bwlp/inc/bootentry.inc.php index 930f4413..010b660c 100644 --- a/modules-available/serversetup-bwlp/inc/bootentry.inc.php +++ b/modules-available/serversetup-bwlp/inc/bootentry.inc.php @@ -14,7 +14,9 @@ abstract class BootEntry } } - public abstract function toScript($failLabel); + public abstract function supportsMode($mode); + + public abstract function toScript($failLabel, $mode); public abstract function toArray(); @@ -46,9 +48,19 @@ abstract class BootEntry public static function newStandardBootEntry($initData) { - if (empty($initData['executable'])) - return null; - return new StandardBootEntry($initData); + $ret = new StandardBootEntry($initData); + $list = []; + if ($ret->arch() !== StandardBootEntry::EFI) { + $list[] = StandardBootEntry::BIOS; + } + if ($ret->arch() === StandardBootEntry::EFI || $ret->arch() === StandardBootEntry::BOTH) { + $list[] = StandardBootEntry::EFI; + } + foreach ($list as $mode) { + if (empty($initData['executable'][$mode])) + return null; + } + return $ret; } public static function newCustomBootEntry($initData) @@ -83,6 +95,12 @@ class StandardBootEntry extends BootEntry protected $replace; protected $autoUnload; protected $resetConsole; + protected $arch; // true == available, false == not available + + const BIOS = 'PCBIOS'; // Only valid for legacy BIOS boot + const EFI = 'EFI'; // Only valid for EFI boot + const BOTH = 'PCBIOS-EFI'; // Supports both via distinct entry + const AGNOSTIC = 'agnostic'; // Supports both via same entry (PCBIOS entry) public function __construct($data = false) { @@ -109,38 +127,70 @@ class StandardBootEntry extends BootEntry } else { parent::__construct($data); } + // Convert legacy DB format + foreach (['executable', 'initRd', 'commandLine', 'replace', 'autoUnload', 'resetConsole'] as $key) { + if (!is_array($this->{$key})) { + $this->{$key} = [ 'PCBIOS' => $this->{$key}, 'EFI' => '' ]; + } + } + if ($this->arch === null) { + $this->arch = self::AGNOSTIC; + } + } + + public function arch() + { + return $this->arch; + } + + public function supportsMode($mode) + { + if ($mode === $this->arch || $this->arch === self::AGNOSTIC) + return true; + if ($mode === self::BIOS || $mode === self::EFI) { + return $this->arch === self::BOTH; + } + error_log('Unknown iPXE platform: ' . $mode); + return false; } - public function toScript($failLabel) + public function toScript($failLabel, $mode) { + if (!$this->supportsMode($mode)) { + return "prompt Entry doesn't have an executable for mode $mode\n"; + } + if ($this->arch === self::AGNOSTIC) { + $mode = self::BIOS; + } + $script = ''; - if ($this->resetConsole) { + if ($this->resetConsole[$mode]) { $script .= "console ||\n"; } - if (!empty($this->initRd)) { + if (!empty($this->initRd[$mode])) { $script .= "imgfree ||\n"; - if (!is_array($this->initRd)) { - $script .= "initrd {$this->initRd} || goto $failLabel\n"; + if (!is_array($this->initRd[$mode])) { + $script .= "initrd {$this->initRd[$mode]} || goto $failLabel\n"; } else { - foreach ($this->initRd as $initrd) { + foreach ($this->initRd[$mode] as $initrd) { $script .= "initrd $initrd || goto $failLabel\n"; } } } $script .= "boot "; - if ($this->autoUnload) { + if ($this->autoUnload[$mode]) { $script .= "-a "; } - if ($this->replace) { + if ($this->replace[$mode]) { $script .= "-r "; } - $script .= "{$this->executable}"; - $rdBase = basename($this->initRd); - if (!empty($this->commandLine)) { - $script .= " initrd=$rdBase {$this->commandLine}"; + $script .= $this->executable[$mode]; + $rdBase = basename($this->initRd[$mode]); + if (!empty($this->commandLine[$mode])) { + $script .= " initrd=$rdBase {$this->commandLine[$mode]}"; } $script .= " || goto $failLabel\n"; - if ($this->resetConsole) { + if ($this->resetConsole[$mode]) { $script .= "goto start ||\n"; } return $script; @@ -148,14 +198,19 @@ class StandardBootEntry extends BootEntry public function addFormFields(&$array) { - $array['entry'] = [ - 'executable' => $this->executable, - 'initRd' => $this->initRd, - 'commandLine' => $this->commandLine, - 'replace_checked' => $this->replace ? 'checked' : '', - 'autoUnload_checked' => $this->autoUnload ? 'checked' : '', - 'resetConsole_checked' => $this->resetConsole ? 'checked' : '', - ]; + $array[$this->arch . '_selected'] = 'selected'; + foreach ([self::BIOS, self::EFI] as $mode) { + $array['entries'][] = [ + 'is' . $mode => true, + 'mode' => $mode, + 'executable' => $this->executable[$mode], + 'initRd' => $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['exec_checked'] = 'checked'; } @@ -168,6 +223,7 @@ class StandardBootEntry extends BootEntry 'replace' => $this->replace, 'autoUnload' => $this->autoUnload, 'resetConsole' => $this->resetConsole, + 'arch' => $this->arch, ]; } } @@ -176,7 +232,12 @@ class CustomBootEntry extends BootEntry { protected $script; - public function toScript($failLabel) + public function supportsMode($mode) + { + return true; + } + + public function toScript($failLabel, $mode) { return str_replace('%fail%', $failLabel, $this->script) . "\n"; } diff --git a/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php b/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php index ed9f0986..56041c20 100644 --- a/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php +++ b/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php @@ -36,11 +36,11 @@ class IPxeMenu } } - public function getMenuDefinition($targetVar) + public function getMenuDefinition($targetVar, $mode) { $str = "menu {$this->title}\n"; foreach ($this->items as $item) { - $str .= $item->getMenuItemScript("m_{$this->menuid}", $this->defaultEntryId); + $str .= $item->getMenuItemScript("m_{$this->menuid}", $this->defaultEntryId, $mode); } if ($this->defaultEntryId === null) { $defaultLabel = "mx_{$this->menuid}_poweroff"; @@ -61,11 +61,11 @@ class IPxeMenu return $str; } - public function getItemsCode() + public function getItemsCode($mode) { $str = ''; foreach ($this->items as $item) { - $str .= $item->getBootEntryScript("m_{$this->menuid}", 'fail'); + $str .= $item->getBootEntryScript("m_{$this->menuid}", 'fail', $mode); $str .= "goto slx_menu\n"; } return $str; diff --git a/modules-available/serversetup-bwlp/inc/menuentry.inc.php b/modules-available/serversetup-bwlp/inc/menuentry.inc.php index 9d9d4163..03b860e8 100644 --- a/modules-available/serversetup-bwlp/inc/menuentry.inc.php +++ b/modules-available/serversetup-bwlp/inc/menuentry.inc.php @@ -58,8 +58,10 @@ class MenuEntry settype($this->menuentryid, 'int'); } - public function getMenuItemScript($lblPrefix, $requestedDefaultId) + public function getMenuItemScript($lblPrefix, $requestedDefaultId, $mode) { + if ($this->bootEntry !== null && !$this->bootEntry->supportsMode($mode)) + return ''; $str = 'item '; if ($this->gap) { $str .= '--gap '; @@ -81,9 +83,9 @@ class MenuEntry return $str . " || prompt Could not create menu item for {$lblPrefix}_{$this->menuentryid}\n"; } - public function getBootEntryScript($lblPrefix, $failLabel) + public function getBootEntryScript($lblPrefix, $failLabel, $mode) { - if ($this->bootEntry === null) + if ($this->bootEntry === null || !$this->bootEntry->supportsMode($mode)) return ''; $str = ":{$lblPrefix}_{$this->menuentryid}\n"; if (!empty($this->md5pass)) { @@ -94,7 +96,7 @@ class MenuEntry . "goto slx_pass_check || goto $failLabel\n" . ":{$lblPrefix}_ok\n"; } - return $str . $this->bootEntry->toScript($failLabel); + return $str . $this->bootEntry->toScript($failLabel, $mode); } /* diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 25a31f06..ba2e5433 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -279,6 +279,7 @@ class Page_ServerSetup extends Page $entry->addFormFields($params); $params['title'] = $row['title']; $params['oldentryid'] = $params['entryid'] = $row['entryid']; + $params['builtin'] = $row['builtin']; } Render::addTemplate('ipxe-new-boot-entry', $params); } @@ -543,7 +544,7 @@ class Page_ServerSetup extends Page } if ($entry === null) { Message::addError('main.empty-field'); - return; + Util::redirect('?do=serversetup&show=bootentry'); } $params = [ 'entryid' => $newId, @@ -560,10 +561,10 @@ class Page_ServerSetup extends Page // Edit existing entry $params['oldid'] = $oldEntryId; Database::exec('UPDATE serversetup_bootentry SET entryid = :entryid, title = :title, data = :data - WHERE entryid = :oldid AND builtin = 0', $params); - // TODO: Redirect to &show=bootentry + WHERE entryid = :oldid', $params); Message::addSuccess('boot-entry-updated', $newId); } + Util::redirect('?do=serversetup&show=bootentry'); } } diff --git a/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html b/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html index fd9e1d72..33de1ee4 100644 --- a/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html +++ b/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html @@ -1,5 +1,11 @@

{{lang_newBootEntryHead}}

+{{#builtin}} +
+ {{lang_editBuiltinWarn}} +
+{{/builtin}} +
{{lang_bootEntryData}} @@ -33,47 +39,69 @@
+
+ + +
-
- - -
-
- - -
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
+
+ {{#entries}} +
+
+
+

{{mode}}

+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ {{/entries}}
@@ -104,5 +132,21 @@ document.addEventListener('DOMContentLoaded', function () { $('#form-' + $(this).val()).show(); }); $('.type-radio[checked]').click(); + var $as = $('#arch-selector'); + $as.change(function() { + var v = $as.val(); + if (v === 'agnostic') { + v = 'PCBIOS'; + $('.arch-heading').hide(); + } else { + $('.arch-heading').show(); + } + var vs = v.split('-'); + var cols = 12 / vs.length; + $('.mode-class').hide(); + for (var i = 0; i < vs.length; ++i) { + $('#col-' + vs[i]).attr('class', 'mode-class col-md-' + cols).show(); + } + }).change(); }); // --> \ No newline at end of file -- cgit v1.2.3-55-g7522 From 1419f851e2870fd5c13bb6ee30305f19d049d01b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 20 Sep 2018 17:08:41 +0200 Subject: [serversetup-bwlp] Implement chain-to-self for missing params --- modules-available/serversetup-bwlp/api.inc.php | 94 ++++++++++------------ .../templates/ipxe-new-boot-entry.html | 6 ++ 2 files changed, 49 insertions(+), 51 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/api.inc.php b/modules-available/serversetup-bwlp/api.inc.php index 52f30440..bc81d35d 100644 --- a/modules-available/serversetup-bwlp/api.inc.php +++ b/modules-available/serversetup-bwlp/api.inc.php @@ -1,6 +1,44 @@ $v) { + $query .= $k . '=' . $v . '&'; + } + $query = substr($query, 0, -1); + echo << 'exit 1', @@ -14,11 +52,8 @@ $ip = $_SERVER['REMOTE_ADDR']; if (substr($ip, 0, 7) === '::ffff:') { $ip = substr($ip, 7); } -$uuid = Request::any('uuid', false, 'string'); $menu = IPxeMenu::forClient($ip, $uuid); -// Get platform - EFI or PCBIOS -$platform = strtoupper(Request::any('platform', 'PCBIOS', 'string')); // Get preferred localboot method, depending on system model $localboot = false; @@ -38,8 +73,8 @@ if ($model === false) { return false; return trim(preg_replace('/\s+/', ' ', $str)); } - $manuf = modfilt(Request::any('manuf', false, 'string')); - $product = modfilt(Request::any('product', false, 'string')); + $manuf = modfilt($manuf); + $product = modfilt($product); if (!empty($product)) { $model = $product; if (!empty($manuf)) { @@ -114,17 +149,13 @@ goto fail # start :init -iseq \${nic} \${} && set nic 0 || - -set ipappend1 ip=\${net\${nic}/ip}:{$serverIp}:\${net\${nic}/gateway}:\${net\${nic}/netmask} -set ipappend2 BOOTIF=01-\${net\${nic}/mac:hexhyp} +set ipappend1 ip=\${ip}:{$serverIp}:\${gateway}:\${netmask} +set ipappend2 BOOTIF=01-\${mac:hexhyp} set serverip $serverIp || # Clean up in case we've been chained to imgfree || -ifopen || - imgfetch --name bg-load /tftp/openslx.png || imgfetch --name bg-menu /tftp/pxe-menu.png || @@ -162,41 +193,12 @@ $output .= $menu->getItemsCode($platform); // TODO: Work out memtest stuff. Needs to be put on server (install/update script) -- PCBIOS only? Chain EFI -> BIOS? /* -:i1 -#console || -echo Welcome to Shell || -shell -goto slx_menu - -:i2 -imgfree || -kernel /boot/default/kernel slxbase=boot/default slxsrv=$serverIp splash BOOTIF=01-\${net\${nic}/mac:hexhyp} || echo Could not download kernel -initrd /boot/default/initramfs-stage31 || echo Could not download initrd -boot -ar || goto fail - -:i3 -chain -ar \${self} || -chain -ar /tftp/undionly.kpxe || goto fail - -:i4 -imgfree || -sanboot --no-describe --drive 0x80 || goto fail :i5 chain -a /tftp/memtest.0 passes=1 onepass || goto membad prompt Memory OK. Press a key. goto init -:i6 -console --left 60 --top 130 --right 67 --bottom 96 --quick --picture bg-load --keep || -echo Welcome to Shell || -shell -goto slx_menu - -:i7 -chain -ar tftp://132.230.4.6/ipxelinux.0 || prompt FAILED PRESS A KEY -goto slx_menu - :i8 set x:int32 0 :again @@ -207,16 +209,6 @@ iseq \${x} 20 || goto again prompt DONE. Press dein Knie. goto slx_menu -:i9 -reboot || -prompt Reboot failed. Press a key. -goto slx_menu - -:i10 -poweroff || -prompt Poweroff failed. Press a key. -goto slx_menu - :membad iseq \${errno} 0x1 || goto memaborted params diff --git a/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html b/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html index 33de1ee4..95802f7a 100644 --- a/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html +++ b/modules-available/serversetup-bwlp/templates/ipxe-new-boot-entry.html @@ -115,6 +115,12 @@
+ {{#builtin}} +
+ {{lang_editBuiltinWarn}} +
+ {{/builtin}} +
@@ -30,8 +30,15 @@ {{/ips}}

- {{lang_bootHint}} + {{lang_recompileHint}}

+
+ + +
\ No newline at end of file -- cgit v1.2.3-55-g7522 From 61bd56dad51354efe37bf0aec80745dd1c09bd4b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 26 Sep 2018 14:49:57 +0200 Subject: [serversetup-bwlp] Detect wether we run our modified iPXE Try not to use special features if stock iPXE is running --- modules-available/serversetup-bwlp/api.inc.php | 54 +++++++++++++++------- .../serversetup-bwlp/inc/ipxemenu.inc.php | 6 +-- .../serversetup-bwlp/inc/menuentry.inc.php | 10 ++-- 3 files changed, 48 insertions(+), 22 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/api.inc.php b/modules-available/serversetup-bwlp/api.inc.php index bc81d35d..4ed316a7 100644 --- a/modules-available/serversetup-bwlp/api.inc.php +++ b/modules-available/serversetup-bwlp/api.inc.php @@ -2,23 +2,40 @@ // Menu mode +$serverIp = Property::getServerIp(); + // Check if required arguments are given; if not, spit out according script and chain to self $uuid = Request::any('uuid', false, 'string'); // Get platform - EFI or PCBIOS $platform = Request::any('platform', false, 'string'); $manuf = Request::any('manuf', false, 'string'); $product = Request::any('product', false, 'string'); +$slxExtensions = Request::any('slx-extensions', false, 'int'); -if ($platform === false || ($uuid === false && $product === false)) { +if ($platform === false || ($uuid === false && $product === false) || $slxExtensions === false) { + error_log(print_r($_SERVER, true)); + sleep(1); $url = parse_url($_SERVER['REQUEST_URI']); - $urlbase = $url['path']; + if (isset($_SERVER['SCRIPT_URI']) && preg_match('#(\w+://[^/]+)#', $_SERVER['SCRIPT_URI'], $out)) { + $urlbase = $out[1]; + } elseif (isset($_SERVER['REQUEST_SCHEME']) && isset($_SERVER['SERVER_NAME'])) { + $urlbase = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME']; + } elseif (isset($_SERVER['REQUEST_SCHEME']) && isset($_SERVER['SERVER_ADDR'])) { + $urlbase = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_ADDR']; + } else { + $urlbase = 'http://' . $serverIp; + } + $urlbase .= $url['path']; if (empty($url['query'])) { $arr = []; } else { parse_str($url['query'], $arr); - $arr = array_map('urlencode', $arr); + foreach ($arr as &$v) { + $v = urlencode($v); + } + unset($v); } - $arr['uuid'] = '${uuid:uristring}'; + $arr['uuid'] = '${uuid}'; $arr['mac'] = '${mac}'; $arr['manuf'] = '${manufacturer:uristring}'; $arr['product'] = '${product:uristring}'; @@ -27,11 +44,16 @@ if ($platform === false || ($uuid === false && $product === false)) { foreach ($arr as $k => $v) { $query .= $k . '=' . $v . '&'; } - $query = substr($query, 0, -1); + //$query = substr($query, 0, -1); echo << 'sanboot --no-describe', ]; -$serverIp = Property::getServerIp(); - $ip = $_SERVER['REMOTE_ADDR']; if (substr($ip, 0, 7) === '::ffff:') { $ip = substr($ip, 7); @@ -109,7 +129,11 @@ if (isset($BOOT_METHODS[$localboot])) { $BOOT_METHODS = array_reverse($BOOT_METHODS); } -// TODO: Feature check for our own iPXE extensions, stay compatible to stock iPXE +if ($slxExtensions) { + $slxConsoleUpdate = '--update'; +} else { + $slxConsoleUpdate = ''; +} $output = <<getMenuDefinition('target', $platform); +$output .= $menu->getMenuDefinition('target', $platform, $slxExtensions); $output .= <<getItemsCode($platform); -// TODO: Work out memtest stuff. Needs to be put on server (install/update script) -- PCBIOS only? Chain EFI -> BIOS? - /* :i5 @@ -202,8 +224,8 @@ goto init :i8 set x:int32 0 :again -console --left 60 --top 130 --right 67 --bottom 96 --picture bg-load --keep --quick || -console --left 55 --top 88 --right 63 --bottom 64 --picture bg-menu --keep --quick || +console --left 60 --top 130 --right 67 --bottom 96 --picture bg-load --keep || +console --left 55 --top 88 --right 63 --bottom 64 --picture bg-menu --keep || inc x iseq \${x} 20 || goto again prompt DONE. Press dein Knie. diff --git a/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php b/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php index 56041c20..6429a2a7 100644 --- a/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php +++ b/modules-available/serversetup-bwlp/inc/ipxemenu.inc.php @@ -36,11 +36,11 @@ class IPxeMenu } } - public function getMenuDefinition($targetVar, $mode) + public function getMenuDefinition($targetVar, $mode, $slxExtensions) { - $str = "menu {$this->title}\n"; + $str = "menu -- {$this->title}\n"; foreach ($this->items as $item) { - $str .= $item->getMenuItemScript("m_{$this->menuid}", $this->defaultEntryId, $mode); + $str .= $item->getMenuItemScript("m_{$this->menuid}", $this->defaultEntryId, $mode, $slxExtensions); } if ($this->defaultEntryId === null) { $defaultLabel = "mx_{$this->menuid}_poweroff"; diff --git a/modules-available/serversetup-bwlp/inc/menuentry.inc.php b/modules-available/serversetup-bwlp/inc/menuentry.inc.php index 03b860e8..d243fd23 100644 --- a/modules-available/serversetup-bwlp/inc/menuentry.inc.php +++ b/modules-available/serversetup-bwlp/inc/menuentry.inc.php @@ -58,7 +58,7 @@ class MenuEntry settype($this->menuentryid, 'int'); } - public function getMenuItemScript($lblPrefix, $requestedDefaultId, $mode) + public function getMenuItemScript($lblPrefix, $requestedDefaultId, $mode, $slxExtensions) { if ($this->bootEntry !== null && !$this->bootEntry->supportsMode($mode)) return ''; @@ -66,7 +66,7 @@ class MenuEntry if ($this->gap) { $str .= '--gap '; } else { - if ($this->hidden) { + if ($this->hidden && $slxExtensions) { if ($this->hotkey === false) return ''; // Hidden entries without hotkey are illegal $str .= '--hidden '; @@ -79,7 +79,11 @@ class MenuEntry } $str .= "{$lblPrefix}_{$this->menuentryid} "; } - $str .= $this->title; + if (empty($this->title)) { + $str .= '${}'; + } else { + $str .= $this->title; + } return $str . " || prompt Could not create menu item for {$lblPrefix}_{$this->menuentryid}\n"; } -- cgit v1.2.3-55-g7522 From 84e147f238286c1dff78b5f9b5488167293f6d35 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 26 Sep 2018 14:50:32 +0200 Subject: [serversetup-bwlp] Don't allow recompilation if already in progress --- modules-available/serversetup-bwlp/page.inc.php | 56 +++++++++++++--------- .../serversetup-bwlp/templates/ipaddress.html | 4 +- .../serversetup-bwlp/templates/ipxe_update.html | 5 +- 3 files changed, 40 insertions(+), 25 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 6b1d20b6..4ebb7530 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -3,11 +3,26 @@ class Page_ServerSetup extends Page { - private $taskStatus; + private $addrListTask; + private $compileTask = null; private $currentAddress; private $currentMenu; private $hasIpSet = false; + private function getCompileTask() + { + if ($this->compileTask !== null) + return $this->compileTask; + $this->compileTask = Property::get('ipxe-task-id'); + if ($this->compileTask !== false) { + $this->compileTask = Taskmanager::status($this->compileTask); + if (!Taskmanager::isTask($this->compileTask) || Taskmanager::isFinished($this->compileTask)) { + $this->compileTask = false; + } + } + return $this->compileTask; + } + protected function doPreprocess() { User::load(); @@ -39,7 +54,9 @@ class Page_ServerSetup extends Page if ($action === 'compile') { User::assertPermission("edit.address"); - Trigger::ipxe(); + if ($this->getCompileTask() === false) { + Trigger::ipxe(); + } Util::redirect('?do=serversetup'); } @@ -100,13 +117,7 @@ class Page_ServerSetup extends Page { Render::addTemplate("heading"); - $task = Property::get('ipxe-task-id'); - if ($task !== false) { - $task = Taskmanager::status($task); - if (!Taskmanager::isTask($task) || Taskmanager::isFinished($task)) { - $task = false; - } - } + $task = $this->getCompileTask(); if ($task !== false) { Render::addTemplate('ipxe_update', array('taskid' => $task['id'])); } @@ -291,8 +302,9 @@ class Page_ServerSetup extends Page private function showEditAddress() { Render::addTemplate('ipaddress', array( - 'ips' => $this->taskStatus['data']['addresses'], + 'ips' => $this->addrListTask['data']['addresses'], 'chooseHintClass' => $this->hasIpSet ? '' : 'alert alert-danger', + 'disabled' => ($this->getCompileTask() === false) ? '' : 'disabled', )); } @@ -300,27 +312,27 @@ class Page_ServerSetup extends Page private function getLocalAddresses() { - $this->taskStatus = Taskmanager::submit('LocalAddressesList', array()); + $this->addrListTask = Taskmanager::submit('LocalAddressesList', array()); - if ($this->taskStatus === false) { - $this->taskStatus['data']['addresses'] = false; + if ($this->addrListTask === false) { + $this->addrListTask['data']['addresses'] = false; return false; } - if (!Taskmanager::isFinished($this->taskStatus)) { // TODO: Async if just displaying - $this->taskStatus = Taskmanager::waitComplete($this->taskStatus['id'], 4000); + if (!Taskmanager::isFinished($this->addrListTask)) { // TODO: Async if just displaying + $this->addrListTask = Taskmanager::waitComplete($this->addrListTask['id'], 4000); } - if (Taskmanager::isFailed($this->taskStatus) || !isset($this->taskStatus['data']['addresses'])) { - $this->taskStatus['data']['addresses'] = false; + if (Taskmanager::isFailed($this->addrListTask) || !isset($this->addrListTask['data']['addresses'])) { + $this->addrListTask['data']['addresses'] = false; return false; } $sortIp = array(); - foreach (array_keys($this->taskStatus['data']['addresses']) as $key) { - $item = & $this->taskStatus['data']['addresses'][$key]; + foreach (array_keys($this->addrListTask['data']['addresses']) as $key) { + $item = & $this->addrListTask['data']['addresses'][$key]; if (!isset($item['ip']) || !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $item['ip']) || substr($item['ip'], 0, 4) === '127.') { - unset($this->taskStatus['data']['addresses'][$key]); + unset($this->addrListTask['data']['addresses'][$key]); continue; } if ($this->currentAddress === $item['ip']) { @@ -330,7 +342,7 @@ class Page_ServerSetup extends Page $sortIp[] = $item['ip']; } unset($item); - array_multisort($sortIp, SORT_STRING, $this->taskStatus['data']['addresses']); + array_multisort($sortIp, SORT_STRING, $this->addrListTask['data']['addresses']); return true; } @@ -483,7 +495,7 @@ class Page_ServerSetup extends Page { $newAddress = Request::post('ip', 'none', 'string'); $valid = false; - foreach ($this->taskStatus['data']['addresses'] as $item) { + foreach ($this->addrListTask['data']['addresses'] as $item) { if ($item['ip'] !== $newAddress) continue; $valid = true; diff --git a/modules-available/serversetup-bwlp/templates/ipaddress.html b/modules-available/serversetup-bwlp/templates/ipaddress.html index ef8cc914..ea19c417 100644 --- a/modules-available/serversetup-bwlp/templates/ipaddress.html +++ b/modules-available/serversetup-bwlp/templates/ipaddress.html @@ -20,7 +20,7 @@ {{/default}} {{^default}} - @@ -35,7 +35,7 @@
- diff --git a/modules-available/serversetup-bwlp/templates/ipxe_update.html b/modules-available/serversetup-bwlp/templates/ipxe_update.html index c5aafa1c..71611085 100644 --- a/modules-available/serversetup-bwlp/templates/ipxe_update.html +++ b/modules-available/serversetup-bwlp/templates/ipxe_update.html @@ -14,7 +14,7 @@ {{lang_generationFailed}} -
{{lang_menuGeneration}}
+
{{lang_menuGeneration}}
@@ -27,6 +27,9 @@ if (task.data.pxeDone) $('#built-pxe').removeClass('invisible'); if (task.data.usbDone) $('#built-usb').removeClass('invisible'); } + if (task.statusCode === 'TASK_ERROR' || task.statusCode === 'TASK_FINISHED') { + $('#tm-compile-div').find('pre').hide(); + } if (task.statusCode === 'TASK_ERROR') { var $gf = $('#genfailed'); if (task.data && task.data.errors) { -- cgit v1.2.3-55-g7522 From 2f3f7ba4844ca4acff33569f3fa46ccf6301c92f Mon Sep 17 00:00:00 2001 From: Udo Walter Date: Thu, 4 Oct 2018 03:19:44 +0200 Subject: [serversetup-bwlp] bootmenu-edit UI improvements - add cancel button - add checkbox to hide menu entries - add button to remove menu entries - hide input fields not relevant for spacers - add modal to select an entry that shows some information about the selected entry --- modules-available/serversetup-bwlp/page.inc.php | 6 +- .../serversetup-bwlp/templates/menu-edit.html | 269 ++++++++++++++------- 2 files changed, 189 insertions(+), 86 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 4ebb7530..615bac64 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -261,7 +261,11 @@ class Page_ServerSetup extends Page $menu['entries'] = Database::queryAll("SELECT menuentryid, entryid, hotkey, title, hidden, sortval, plainpass FROM serversetup_menuentry WHERE menuid = :id ORDER BY sortval ASC", compact('id')); $menu['keys'] = array_map(function ($item) { return ['key' => $item]; }, MenuEntry::getKeyList()); - $menu['entrylist'] = Database::queryAll("SELECT entryid, title, hotkey FROM serversetup_bootentry ORDER BY title ASC"); + $menu['entrylist'] = Database::queryAll("SELECT entryid, title, hotkey, data FROM serversetup_bootentry ORDER BY title ASC"); + foreach ($menu['entrylist'] as &$bootentry) { + $bootentry['json'] = $bootentry['data']; + $bootentry['data'] = json_decode($bootentry['data']); + } foreach ($menu['entries'] as &$entry) { $entry['isdefault'] = ($entry['menuentryid'] == $menu['defaultentryid']); // TODO: plainpass only when permissions diff --git a/modules-available/serversetup-bwlp/templates/menu-edit.html b/modules-available/serversetup-bwlp/templates/menu-edit.html index c0a353b4..e695ef20 100644 --- a/modules-available/serversetup-bwlp/templates/menu-edit.html +++ b/modules-available/serversetup-bwlp/templates/menu-edit.html @@ -7,7 +7,7 @@
{{title}} {{^title}} - {{lang_newMenu}} + {{lang_newMenu}} {{/title}}
@@ -50,86 +50,151 @@ - - - + + + - - + + + + {{#entries}} - - - - - - + + + + - + + - - {{#entryid}} - - {{/entryid}} - - - + + + + + + + + + + {{/entries}}
{{lang_entryId}}{{lang_entryId}} {{lang_title}}{{lang_hotkey}}{{lang_password}}{{lang_hotkey}}{{lang_password}}
- - -
- - -
-
+ + + +
+ + +
+
+ + - - - - - -
+ + + + + + +
+ + +
+
+ +
-
+
+
+
+ {{lang_cancel}}
+ +
- @@ -141,38 +206,52 @@ -
+ - + + - - + + + +
+ + +
+ + + \ No newline at end of file -- cgit v1.2.3-55-g7522 From 093d16682743eb1502819bb6c127afeac65d4ede Mon Sep 17 00:00:00 2001 From: Udo Walter Date: Fri, 9 Nov 2018 02:46:50 +0100 Subject: [serversetup-bwlp] add location selection to menu edit + ui improvements - add multiselect to select the locations - add architecture information to the bootentry select modal - add glyphicon to the hide entry column header - default menu is now selected in the menu list - fixed error when creating a menu (isdefault has no default value) and when creating a menu without bootentries --- modules-available/serversetup-bwlp/config.json | 3 +- modules-available/serversetup-bwlp/page.inc.php | 221 ++++++++++++++------- .../serversetup-bwlp/templates/menu-edit.html | 124 +++++++++--- .../serversetup-bwlp/templates/menu-list.html | 11 +- 4 files changed, 261 insertions(+), 98 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/config.json b/modules-available/serversetup-bwlp/config.json index ff485760..8b3ce2a3 100644 --- a/modules-available/serversetup-bwlp/config.json +++ b/modules-available/serversetup-bwlp/config.json @@ -2,6 +2,7 @@ "category": "main.settings-server", "dependencies" : [ "locations", - "js_jqueryui" + "js_jqueryui", + "bootstrap_multiselect" ] } \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 615bac64..87c57dea 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -87,6 +87,11 @@ class Page_ServerSetup extends Page $this->deleteMenu(); } + if ($action === 'setDefaultMenu') { + User::assertPermission('ipxe.menu.edit', 0); + $this->setDefaultMenu(); + } + if (Request::isPost()) { Util::redirect('?do=serversetup'); } @@ -213,7 +218,8 @@ class Page_ServerSetup extends Page Render::addTemplate('menu-list', array( 'menuTable' => $menuTable, - 'allowAddMenu' => $allowAddMenu + 'allowAddMenu' => $allowAddMenu, + 'showSetDefault' => User::hasPermission('ipxe.menu.edit', 0) )); } @@ -252,11 +258,15 @@ class Page_ServerSetup extends Page Message::addError('invalid-menu-id', $id); Util::redirect('?do=serversetup&show=menu'); } - if (!$this->hasMenuPermission($id, 'ipxe.menu.edit')) { + if ($id !== 0 && !$this->hasMenuPermission($id, 'ipxe.menu.edit')) { $menu['readonly'] = 'readonly'; $menu['disabled'] = 'disabled'; $menu['plainpass'] = ''; } + if (!User::hasPermission('ipxe.menu.edit', 0)) { + $menu['globalMenuWarning'] = true; + } + $menu['timeout'] = round($menu['timeoutms'] / 1000); $menu['entries'] = Database::queryAll("SELECT menuentryid, entryid, hotkey, title, hidden, sortval, plainpass FROM serversetup_menuentry WHERE menuid = :id ORDER BY sortval ASC", compact('id')); @@ -264,13 +274,50 @@ class Page_ServerSetup extends Page $menu['entrylist'] = Database::queryAll("SELECT entryid, title, hotkey, data FROM serversetup_bootentry ORDER BY title ASC"); foreach ($menu['entrylist'] as &$bootentry) { $bootentry['json'] = $bootentry['data']; - $bootentry['data'] = json_decode($bootentry['data']); + $bootentry['data'] = json_decode($bootentry['data'], true); + if (array_key_exists('arch', $bootentry['data'])) { + $bootentry['data']['PCBIOS'] = array('executable' => $bootentry['data']['executable']['PCBIOS'], + 'initRd' => $bootentry['data']['initRd']['PCBIOS'], + 'commandLine' => $bootentry['data']['commandLine']['PCBIOS']); + $bootentry['data']['EFI'] = array('executable' => $bootentry['data']['executable']['EFI'], + 'initRd' => $bootentry['data']['initRd']['EFI'], + 'commandLine' => $bootentry['data']['commandLine']['EFI']); + + if ($bootentry['data']['arch'] === 'PCBIOS') { + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_biosOnly', true); + unset($bootentry['data']['EFI']); + } else if ($bootentry['data']['arch'] === 'EFI') { + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_efiOnly', true); + unset($bootentry['data']['PCBIOS']); + } else { + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_archBoth', true); + } + + } else { + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_archAgnostic', true); + $bootentry['data']['archAgnostic'] = array('executable' => $bootentry['data']['executable'], + 'initRd' => $bootentry['data']['initRd'], + 'commandLine' => $bootentry['data']['commandLine']); + } } foreach ($menu['entries'] as &$entry) { $entry['isdefault'] = ($entry['menuentryid'] == $menu['defaultentryid']); // TODO: plainpass only when permissions } // TODO: Make assigned locations editable + + $currentLocations = Database::queryColumnArray('SELECT locationid FROM serversetup_menu_location + WHERE menuid = :menuid', array('menuid' => $id)); + $menu['locations'] = Location::getLocations($currentLocations); + + // if user has no permission to edit for this location, disable the location in the select + $allowedEditLocations = User::getAllowedLocations('ipxe.menu.edit'); + foreach ($menu['locations'] as &$loc) { + if (!in_array($loc["locationid"], $allowedEditLocations)) { + $loc["disabled"] = "disabled"; + } + } + Permission::addGlobalTags($menu['perms'], 0, ['ipxe.menu.edit']); Render::addTemplate('menu-edit', $menu); } @@ -300,6 +347,7 @@ class Page_ServerSetup extends Page $params['oldentryid'] = $params['entryid'] = $row['entryid']; $params['builtin'] = $row['builtin']; } + Render::addTemplate('ipxe-new-boot-entry', $params); } @@ -361,6 +409,17 @@ class Page_ServerSetup extends Page Message::addSuccess('bootentry-deleted'); } + private function setDefaultMenu() + { + $id = Request::post('menuid', false, 'int'); + if ($id === false) { + Message::addError('main.parameter-missing', 'menuid'); + return; + } + Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); + Message::addSuccess('menu-set-default'); + } + private function deleteMenu() { $id = Request::post('deleteid', false, 'int'); @@ -374,7 +433,6 @@ class Page_ServerSetup extends Page } Database::exec("DELETE FROM serversetup_menu WHERE menuid = :menuid", array("menuid" => $id)); Message::addSuccess('menu-deleted'); - } private function saveMenu() @@ -384,13 +442,30 @@ class Page_ServerSetup extends Page Message::addError('main.parameter-missing', 'menuid'); return; } - // TODO: Validate new locations to be saved (and actually save them) + + $locationids = Request::post('locations', [], "ARRAY"); + // check if the user is allowed to edit the menu on the affected locations + $allowedEditLocations = User::getAllowedLocations('ipxe.menu.edit'); + $currentLocations = Database::queryColumnArray('SELECT locationid FROM serversetup_menu_location + WHERE menuid = :menuid', array('menuid' => $id)); + // permission denied if the user tries to assign or remove a menu to/from locations he has no edit rights for + // or if the user tries to save a menu without locations but does not have the permission for the root location (0) + if (!in_array(0, $allowedEditLocations) + && ( + (!empty(array_diff($locationids, $allowedEditLocations)) && !empty(array_diff($currentLocations, $allowedEditLocations))) + || empty($locationids) + ) + ) { + Message::addError('main.no-permission'); + Util::redirect('?do=serversetup'); + } + $insertParams = [ 'title' => IPxe::sanitizeIpxeString(Request::post('title', '', 'string')), 'timeoutms' => abs(Request::post('timeout', 0, 'int') * 1000), ]; if ($id === 0) { - Database::exec("INSERT INTO serversetup_menu (title, timeoutms) VALUES (:title, :timeoutms)", $insertParams); + Database::exec("INSERT INTO serversetup_menu (title, timeoutms, isdefault) VALUES (:title, :timeoutms, 0)", $insertParams); $menu['menuid'] = $id = Database::lastInsertId(); } else { $menu = Database::queryFirst("SELECT m.menuid, GROUP_CONCAT(l.locationid) AS locations @@ -410,88 +485,98 @@ class Page_ServerSetup extends Page WHERE menuid = :menuid', $insertParams); } - if (User::hasPermission('ipxe.menu.edit', 0) - && Request::post('defmenu', false, 'boolean')) { - Database::exec('UPDATE serversetup_menu SET isdefault = (menuid = :menuid)', ['menuid' => $id]); - } - $keepIds = []; $entries = Request::post('entry', false, 'array'); $wantedDefaultEntryId = Request::post('defaultentry', null, 'string'); $defaultEntryId = null; - foreach ($entries as $key => $entry) { - if (!isset($entry['sortval'])) { - error_log(print_r($entry, true)); - continue; - } - // Fallback defaults - $entry += [ - 'entryid' => null, - 'title' => '', - 'hidden' => 0, - 'plainpass' => '', - ]; - $params = [ - 'title' => IPxe::sanitizeIpxeString($entry['title']), - 'sortval' => (int)$entry['sortval'], - 'menuid' => $menu['menuid'], - ]; - if (empty($entry['entryid'])) { - // Spacer - $params += [ + if ($entries) { + foreach ($entries as $key => $entry) { + if (!isset($entry['sortval'])) { + error_log(print_r($entry, true)); + continue; + } + // Fallback defaults + $entry += [ 'entryid' => null, - 'hotkey' => '', - 'hidden' => 0, // Doesn't make any sense - 'plainpass' => '', // Doesn't make any sense + 'title' => '', + 'hidden' => 0, + 'plainpass' => '', ]; - } else { - $params += [ - 'entryid' => $entry['entryid'], // TODO validate? - 'hotkey' => MenuEntry::filterKeyName($entry['hotkey']), - 'hidden' => (int)$entry['hidden'], // TODO (needs hotkey to make sense) - 'plainpass' => $entry['plainpass'], + $params = [ + 'title' => IPxe::sanitizeIpxeString($entry['title']), + 'sortval' => (int)$entry['sortval'], + 'menuid' => $menu['menuid'], ]; - } - if (is_numeric($key)) { - if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key - $defaultEntryId = $key; + if (empty($entry['entryid'])) { + // Spacer + $params += [ + 'entryid' => null, + 'hotkey' => '', + 'hidden' => 0, // Doesn't make any sense + 'plainpass' => '', // Doesn't make any sense + ]; + } else { + $params += [ + 'entryid' => $entry['entryid'], // TODO validate? + 'hotkey' => MenuEntry::filterKeyName($entry['hotkey']), + 'hidden' => (int)$entry['hidden'], // TODO (needs hotkey to make sense) + 'plainpass' => $entry['plainpass'], + ]; } - $keepIds[] = $key; - $params['menuentryid'] = $key; - $params['md5pass'] = IPxe::makeMd5Pass($entry['plainpass'], $key); - $ret = Database::exec('UPDATE serversetup_menuentry + if (is_numeric($key)) { + if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key + $defaultEntryId = $key; + } + $keepIds[] = $key; + $params['menuentryid'] = $key; + $params['md5pass'] = IPxe::makeMd5Pass($entry['plainpass'], $key); + $ret = Database::exec('UPDATE serversetup_menuentry SET entryid = :entryid, hotkey = :hotkey, title = :title, hidden = :hidden, sortval = :sortval, plainpass = :plainpass, md5pass = :md5pass WHERE menuid = :menuid AND menuentryid = :menuentryid', $params, true); - } else { - $ret = Database::exec("INSERT INTO serversetup_menuentry + } else { + $ret = Database::exec("INSERT INTO serversetup_menuentry (menuid, entryid, hotkey, title, hidden, sortval, plainpass, md5pass) VALUES (:menuid, :entryid, :hotkey, :title, :hidden, :sortval, :plainpass, '')", $params, true); - if ($ret) { - $newKey = Database::lastInsertId(); - if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key - $defaultEntryId = $newKey; - } - $keepIds[] = (int)$newKey; - if (!empty($entry['plainpass'])) { - Database::exec('UPDATE serversetup_menuentry SET md5pass = :md5pass WHERE menuentryid = :id', [ - 'md5pass' => IPxe::makeMd5Pass($entry['plainpass'], $newKey), - 'id' => $newKey, - ]); + if ($ret) { + $newKey = Database::lastInsertId(); + if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key + $defaultEntryId = $newKey; + } + $keepIds[] = (int)$newKey; + if (!empty($entry['plainpass'])) { + Database::exec('UPDATE serversetup_menuentry SET md5pass = :md5pass WHERE menuentryid = :id', [ + 'md5pass' => IPxe::makeMd5Pass($entry['plainpass'], $newKey), + 'id' => $newKey, + ]); + } } } + + if ($ret === false) { + Message::addWarning('error-saving-entry', $entry['title'], Database::lastError()); + } } + Database::exec('DELETE FROM serversetup_menuentry WHERE menuid = :menuid AND menuentryid NOT IN (:keep)', + ['menuid' => $menu['menuid'], 'keep' => $keepIds]); + // Set default entry + Database::exec('UPDATE serversetup_menu SET defaultentryid = :default WHERE menuid = :menuid', + ['menuid' => $menu['menuid'], 'default' => $defaultEntryId]); + } else { + Database::exec('DELETE FROM serversetup_menuentry WHERE menuid = :menuid', ['menuid' => $menu['menuid']]); + Database::exec('UPDATE serversetup_menu SET defaultentryid = NULL WHERE menuid = :menuid', ['menuid' => $menu['menuid']]); + } - if ($ret === false) { - Message::addWarning('error-saving-entry', $entry['title'], Database::lastError()); + Database::exec('DELETE FROM serversetup_menu_location WHERE menuid = :menuid', ['menuid' => $menu['menuid']]); + if (!empty($locationids)) { + Database::exec('DELETE FROM serversetup_menu_location WHERE locationid IN (:locationids)', ['locationids' => $locationids]); + foreach ($locationids as $locationid) { + Database::exec('INSERT INTO serversetup_menu_location (menuid, locationid) VALUES (:menuid, :locationid)', + ['menuid' => $menu['menuid'], 'locationid' => $locationid]); } } - Database::exec('DELETE FROM serversetup_menuentry WHERE menuid = :menuid AND menuentryid NOT IN (:keep)', - ['menuid' => $menu['menuid'], 'keep' => $keepIds]); - // Set default entry - Database::exec('UPDATE serversetup_menu SET defaultentryid = :default WHERE menuid = :menuid', - ['menuid' => $menu['menuid'], 'default' => $defaultEntryId]); + Message::addSuccess('menu-saved'); } diff --git a/modules-available/serversetup-bwlp/templates/menu-edit.html b/modules-available/serversetup-bwlp/templates/menu-edit.html index e695ef20..09814da9 100644 --- a/modules-available/serversetup-bwlp/templates/menu-edit.html +++ b/modules-available/serversetup-bwlp/templates/menu-edit.html @@ -38,12 +38,17 @@
+
-
- - -
+ + {{#globalMenuWarning}} + + {{/globalMenuWarning}}
@@ -56,7 +61,7 @@ {{lang_title}} {{lang_hotkey}} {{lang_password}} - + @@ -124,7 +129,7 @@
{{lang_cancel}} - @@ -154,29 +159,69 @@
{{#data}} {{#script}} -
- -
{{.}}
-
+
+ +
{{.}}
+
+ {{/script}} + {{^script}} +
+ +
{{arch}}
+
+ {{#archAgnostic}} +
+ +
{{executable}}
+
+
+ +
{{initRd}}
+
+
+ +
{{commandLine}}
+
+ {{/archAgnostic}} + {{#PCBIOS}} +
+
PCBIOS
+
+
+ +
{{executable}}
+
+
+ +
{{initRd}}
+
+
+ +
{{commandLine}}
+
+
+
+ {{/PCBIOS}} + {{#EFI}} +
+
EFI
+
+
+ +
{{executable}}
+
+
+ +
{{initRd}}
+
+
+ +
{{commandLine}}
+
+
+
+ {{/EFI}} {{/script}} - {{#executable}} -
- -
{{.}}
-
- {{/executable}} - {{#initRd}} -
- -
{{.}}
-
- {{/initRd}} - {{#commandLine}} -
- -
{{.}}
-
- {{/commandLine}} {{/data}} {{/entrylist}} @@ -244,6 +289,29 @@ var spacerText = "{{lang_spacer}}"; document.addEventListener("DOMContentLoaded", function() { + var locationSelect = $('#panel-locations'); + locationSelect.multiselect({numberDisplayed: 1}); + var globalMenuWarning = $('#global-menu-warning'); + if (globalMenuWarning.length) { + var saveButton = $('#save-button'); + if (locationSelect.val() !== null) { + saveButton.prop('disabled', false); + globalMenuWarning.hide(); + } else { + saveButton.prop('disabled', true); + globalMenuWarning.show(); + } + locationSelect.change(function () { + if ($(this).val() !== null) { + saveButton.prop('disabled', false); + globalMenuWarning.hide(); + } else { + saveButton.prop('disabled', true); + globalMenuWarning.show(); + } + }); + } + function reassignSortValues() { var startValue = 1; $('.sort-val').each(function(index, element) { diff --git a/modules-available/serversetup-bwlp/templates/menu-list.html b/modules-available/serversetup-bwlp/templates/menu-list.html index 1f190bb7..5d8ee184 100644 --- a/modules-available/serversetup-bwlp/templates/menu-list.html +++ b/modules-available/serversetup-bwlp/templates/menu-list.html @@ -20,13 +20,22 @@ {{locationCount}} + {{^isdefault}} +
+ + + +
+ {{/isdefault}} {{#isdefault}} {{/isdefault}} {{#allowEdit}} - + {{/allowEdit}} -- cgit v1.2.3-55-g7522 From 6812e85e11718116f097f493419c489c41660c77 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 21 Nov 2018 14:53:14 +0100 Subject: [serversetup-bwlp] Compilation front-end --- modules-available/serversetup-bwlp/page.inc.php | 8 ++++- .../serversetup-bwlp/templates/ipxe_update.html | 37 +++++++++++++++------- 2 files changed, 32 insertions(+), 13 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 87c57dea..5f927173 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -124,7 +124,13 @@ class Page_ServerSetup extends Page $task = $this->getCompileTask(); if ($task !== false) { - Render::addTemplate('ipxe_update', array('taskid' => $task['id'])); + $files = []; + if ($task['data'] && $task['data']['files']) { + foreach ($task['data']['files'] as $k => $v) { + $files[] = ['name' => $k, 'namehyphen' => str_replace(['/', '.'], '-', $k)]; + } + } + Render::addTemplate('ipxe_update', array('taskid' => $task['id'], 'files' => $files)); } switch (Request::get('show')) { diff --git a/modules-available/serversetup-bwlp/templates/ipxe_update.html b/modules-available/serversetup-bwlp/templates/ipxe_update.html index 71611085..344d3905 100644 --- a/modules-available/serversetup-bwlp/templates/ipxe_update.html +++ b/modules-available/serversetup-bwlp/templates/ipxe_update.html @@ -1,13 +1,13 @@
{{lang_menuGeneration}}
- -
diff --git a/modules-available/serversetup-bwlp/templates/menu-list.html b/modules-available/serversetup-bwlp/templates/menu-list.html index 5d8ee184..67365a33 100644 --- a/modules-available/serversetup-bwlp/templates/menu-list.html +++ b/modules-available/serversetup-bwlp/templates/menu-list.html @@ -21,13 +21,15 @@ {{^isdefault}} -
+ {{#showSetDefault}} +
+ {{/showSetDefault}} {{/isdefault}} {{#isdefault}} -- cgit v1.2.3-55-g7522 From c4210d3241926bb1f0ca86a5f7cf65cd29b9dc12 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 21 Nov 2018 14:58:21 +0100 Subject: [serversetup-bwlp] Add de translations --- .../serversetup-bwlp/lang/de/module.json | 6 ++- .../serversetup-bwlp/lang/de/template-tags.json | 47 ++++++++++++++++++---- 2 files changed, 45 insertions(+), 8 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/lang/de/module.json b/modules-available/serversetup-bwlp/lang/de/module.json index da71d558..6de5076e 100644 --- a/modules-available/serversetup-bwlp/lang/de/module.json +++ b/modules-available/serversetup-bwlp/lang/de/module.json @@ -1,4 +1,8 @@ { "module_name": "iPXE \/ Boot Menu", - "page_title": "PXE- und Boot-Einstellungen" + "page_title": "PXE- und Boot-Einstellungen", + "submenu_address": "Server-Adresse", + "submenu_bootentry": "Booteintr\u00e4ge verwalten", + "submenu_download": "Downloads", + "submenu_menu": "Men\u00fcs verwalten" } \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/lang/de/template-tags.json b/modules-available/serversetup-bwlp/lang/de/template-tags.json index bda890c1..4f255bbd 100644 --- a/modules-available/serversetup-bwlp/lang/de/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/de/template-tags.json @@ -1,24 +1,45 @@ { "lang_active": "Aktiv", - "lang_addBootentry": "Booteintrag hinzufügen", - "lang_addMenu": "Menü hinzufügen", + "lang_add": "Hinzuf\u00fcgen", + "lang_addBootentry": "Booteintrag hinzuf\u00fcgen", + "lang_addMenu": "Men\u00fc hinzuf\u00fcgen", + "lang_archAgnostic": "Architekturunabh\u00e4ngig", + "lang_archBoth": "BIOS und EFI", + "lang_archSelector": "Architekturauswahl", + "lang_biosOnly": "Nur BIOS", "lang_bootAddress": "Boot-Adresse des Servers", "lang_bootBehavior": "Standard-Bootverhalten", - "lang_bootentryTitle": "Booteintrag", + "lang_bootEntryData": "Daten des Booteintrags", "lang_bootHint": "Das Bootmen\u00fc muss nach einer \u00c4nderung der IP-Adresse neu generiert werden. In der Regel geschieht dies automatisch, der Vorgang kann in der Sektion Bootmen\u00fc allerdings auch manuell ausgel\u00f6st werden.", "lang_bootInfo": "Hier k\u00f6nnen Anpassungen am Erscheinungsbild des Bootmen\u00fcs vorgenommen werden.", "lang_bootMenu": "Bootmen\u00fc", "lang_bootMenuCreate": "Bootmen\u00fc erzeugen", + "lang_bootentryDeleteConfirm": "Sind Sie sicher, dass Sie diesen Booteintrag l\u00f6schen wollen?", + "lang_bootentryTitle": "Booteintrag", "lang_chooseIP": "Bitte w\u00e4hlen Sie die IP-Adresse, \u00fcber die der Server von den Clients zum Booten angesprochen werden soll.", + "lang_commandLine": "Command line", "lang_customEntry": "Eigener Eintrag", - "lang_bootentryDeleteConfirm": "Sind Sie sicher, dass Sie diesen Booteintrag löschen wollen?", - "lang_menuDeleteConfirm": "Sind Sie sicher, dass Sie dieses Menü löschen wollen?", "lang_downloadImage": "USB-Image herunterladen", "lang_downloadRufus": "Rufus herunterladen", + "lang_editBuiltinWarn": "Achtung! Sie bearbeiten einen der vorgegebenen Eintr\u00e4ge! Bei einem Update k\u00f6nnten Ihre \u00c4nderungen wieder \u00fcberschrieben werden", + "lang_editMenuHead": "Men\u00fc bearbeiten", + "lang_efiOnly": "Nur EFI", + "lang_entryChooserTitle": "Booteintrag ausw\u00e4hlen", + "lang_entryId": "ID", + "lang_entryTitle": "Bezeichnung", "lang_example": "Beispiel", + "lang_execAutoUnload": "Nach Ausf\u00fchrung entladen (--autofree)", + "lang_execReplace": "Aktuellen iPXE-Stack erstzen (--replace)", + "lang_execResetConsole": "Konsole vor Ausf\u00fchrung zur\u00fccksetzen", + "lang_forceRecompile": "Jetzt neu compilieren", "lang_generationFailed": "Erzeugen des Bootmen\u00fcs fehlgeschlagen. Der Netzwerkboot von bwLehrpool wird wahrscheinlich nicht funktionieren. Wenn Sie den Fehler nicht selbst beheben k\u00f6nnen, melden Sie bitte die Logausgabe an das bwLehrpool-Projekt.", + "lang_globalMenuWarning": "Dieses Men\u00fc ist keinem Raum zugeordnet", + "lang_hotkey": "Hotkey", + "lang_idFormatHint": "(Max. 16 Zeichen, nur a-z 0-9 - _)", + "lang_imageToLoad": "Zu ladendes Image (z.B. Kernel)", + "lang_initRd": "Zu ladendes initramfs", "lang_isDefault": "Standard", - "lang_listOfMenus": "Menüliste", + "lang_listOfMenus": "Men\u00fcliste", "lang_localHDD": "Lokale HDD", "lang_locationCount": "Anzahl Orte", "lang_masterPassword": "Master-Passwort", @@ -27,13 +48,25 @@ "lang_menuCustomHint1": "Hier haben Sie die M\u00f6glichkeit, eigenen Men\u00fc-Code zum angezeigten PXE-Men\u00fc hinzuzuf\u00fcgen, um z.B. auf weitere PXE-Server zu verweisen. Das Format entspricht dem syslinux Men\u00fcformat.", "lang_menuCustomHint2": "Sie k\u00f6nnen ein oder mehrere Eintr\u00e4ge erzeugen. Wenn Sie einen Eintrag erzeugen m\u00f6chten, der automatisch gestartet wird, wenn der Benutzer keine Auswahl t\u00e4tigt, vergeben Sie als", "lang_menuCustomHint3": "und w\u00e4hlen Sie als Standard-Bootverhalten ebenfalls custom.", + "lang_menuDeleteConfirm": "Sind Sie sicher, dass Sie dieses Men\u00fc l\u00f6schen wollen?", "lang_menuDisplayTime": "Anzeigedauer des Men\u00fcs", "lang_menuGeneration": "Erzeugen des Bootmen\u00fcs", - "lang_menuTitle": "Menü", + "lang_menuLocations": "Zugewiesene Orte", + "lang_menuTimeout": "Timeout", + "lang_menuTitle": "Men\u00fc", "lang_moduleHeading": "iPXE \/ Boot Menu", + "lang_newBootEntryHead": "Neuer Booteintrag", + "lang_newMenu": "Neues Men\u00fc", + "lang_none": "(keine)", "lang_pxeBuilt": "PXE-Binary gebaut", + "lang_recompileHint": "iPXE-Binaries jetzt neu kompilieren. Normalerweise wird dieser Vorgang bei \u00c4nderungen automatisch ausgef\u00fchrt. Sollten Bootprobleme auftreten, k\u00f6nnen Sie hier den Vorgang manuell ansto\u00dfen.", + "lang_scriptContent": "Script", "lang_seconds": "Sekunden", "lang_set": "Setzen", + "lang_spacer": "Abstandhalter\/\u00dcberschrift", + "lang_title": "Titel", + "lang_typeExecEntry": "Standardeintrag", + "lang_typeScriptEntry": "Benutzerdefiniertes Script", "lang_usbBuilt": "USB-Image gebaut", "lang_usbImage": "USB-Image", "lang_usbImgHelp": "Mit dem USB-Image k\u00f6nnen Sie einen bootbaren USB-Stick erstellen, \u00fcber den sich bwLehrpool an Rechnern starten l\u00e4sst, die keinen Netzwerkboot unterst\u00fctzen, bzw. f\u00fcr die keine entsprechende DHCP-Konfiguration vorhanden ist. Dies erfordert dann lediglich, dass in der BIOS-Konfiguration des Rechners USB-Boot zugelassen ist. Der Stick dient dabei lediglich als Einstiegspunkt; es ist nach wie vor ein bwLehrpool-Satellitenserver f\u00fcr den eigentlichen Bootvorgang von N\u00f6ten.", -- cgit v1.2.3-55-g7522 From 10092ba591461771c6b68fea9b664e92e6e99c53 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 21 Nov 2018 14:59:03 +0100 Subject: [serversetup-bwlp] Add de translations --- modules-available/serversetup-bwlp/lang/de/messages.json | 14 +++++++++++++- modules-available/serversetup-bwlp/page.inc.php | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/lang/de/messages.json b/modules-available/serversetup-bwlp/lang/de/messages.json index 3e2cc834..2af5cc57 100644 --- a/modules-available/serversetup-bwlp/lang/de/messages.json +++ b/modules-available/serversetup-bwlp/lang/de/messages.json @@ -1,5 +1,17 @@ { + "boot-entry-created": "Booteintrag {{0}} erzeugt", + "boot-entry-updated": "Booteintrag {{0}} aktualisiert", + "bootentry-deleted": "Booteintrag gel\u00f6scht", + "error-saving-entry": "Fehler beim Speichern des Eintrags {{0}}: {{1}}", "image-not-found": "USB-Image nicht gefunden. Generieren Sie das Bootmen\u00fc neu.", + "invalid-boot-entry": "Ung\u00fcltiger Booteintrag: {{0}}", "invalid-ip": "Kein Interface ist auf die Adresse {{0}} konfiguriert", - "no-ip-addr-set": "Bitte w\u00e4hlen Sie die prim\u00e4re IP-Adresse des Servers" + "invalid-menu-id": "Ung\u00fcltige Men\u00fc-ID: {{0}}", + "menu-deleted": "Men\u00fc gel\u00f6scht", + "menu-saved": "Men\u00fc wurde gespeichert", + "menu-set-default": "Standardmen\u00fc wurde gesetzt", + "missing-bootentry-data": "Fehlende Daten f\u00fcr den Booteintrag", + "no-ip-addr-set": "Bitte w\u00e4hlen Sie die prim\u00e4re IP-Adresse des Servers", + "no-such-menu": "Men\u00fc mit ID {{0}} existiert nicht", + "unknown-bootentry-type": "Unbekannter Eintrags-Typ: {{0}}" } \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 5f927173..18ad396a 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -345,7 +345,7 @@ class Page_ServerSetup extends Page } $entry = BootEntry::fromJson($row['data']); if ($entry === null) { - Message::addError('unknown-boot-entry-type', $id); + Message::addError('unknown-bootentry-type', $id); Util::redirect('?do=serversetup'); } $entry->addFormFields($params); @@ -628,7 +628,7 @@ class Page_ServerSetup extends Page } $data = Request::post('entry', false); if (!is_array($data)) { - Message::addError('missing-entry-data'); + Message::addError('missing-bootentry-data'); return; } $type = Request::post('type', false, 'string'); @@ -637,7 +637,7 @@ class Page_ServerSetup extends Page } elseif ($type === 'script') { $entry = BootEntry::newCustomBootEntry($data); } else { - Message::addError('unknown-entry-type', $type); + Message::addError('unknown-bootentry-type', $type); return; } if ($entry === null) { -- cgit v1.2.3-55-g7522 From 8882cfc967e2874f57bb70cd55179fed624e0461 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 23 Nov 2018 13:20:19 +0100 Subject: [serversetup-bwlp] Fix adding boot entry of type exec --- modules-available/serversetup-bwlp/page.inc.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index 18ad396a..8cd20c75 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -335,6 +335,10 @@ class Page_ServerSetup extends Page if ($id === false) { $params['exec_checked'] = 'checked'; $params['entryid'] = 'u-' . dechex(mt_rand(0x1000, 0xffff)) . '-' . dechex(time()); + $params['entries'] = [ + ['mode' => 'PCBIOS'], + ['mode' => 'EFI'], + ]; } else { // Query existing entry $row = Database::queryFirst('SELECT entryid, title, builtin, data FROM serversetup_bootentry -- cgit v1.2.3-55-g7522 From 1729bd0ec9137697a7688c5f556163717abfb54b Mon Sep 17 00:00:00 2001 From: Udo Walter Date: Fri, 23 Nov 2018 16:02:17 +0100 Subject: [serversetup-bwlp] Fix new menu entries not using the preset titles --- .../serversetup-bwlp/templates/menu-edit.html | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/templates/menu-edit.html b/modules-available/serversetup-bwlp/templates/menu-edit.html index 6059333e..2141103f 100644 --- a/modules-available/serversetup-bwlp/templates/menu-edit.html +++ b/modules-available/serversetup-bwlp/templates/menu-edit.html @@ -84,7 +84,7 @@ -
- -- cgit v1.2.3-55-g7522 From 63aa220d849dca2384773bf755358557a1d711c5 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 18 Jan 2019 16:37:17 +0100 Subject: [serversetup-bwlp] Make localboot method configurable --- modules-available/serversetup-bwlp/api.inc.php | 23 ++----- .../serversetup-bwlp/inc/localboot.inc.php | 17 +++++ .../serversetup-bwlp/lang/de/messages.json | 2 + .../serversetup-bwlp/lang/de/module.json | 1 + .../serversetup-bwlp/lang/de/template-tags.json | 6 ++ modules-available/serversetup-bwlp/page.inc.php | 78 ++++++++++++++++++++++ .../serversetup-bwlp/templates/localboot.html | 59 ++++++++++++++++ 7 files changed, 169 insertions(+), 17 deletions(-) create mode 100644 modules-available/serversetup-bwlp/inc/localboot.inc.php create mode 100644 modules-available/serversetup-bwlp/templates/localboot.html (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/api.inc.php b/modules-available/serversetup-bwlp/api.inc.php index 4ed316a7..d089584e 100644 --- a/modules-available/serversetup-bwlp/api.inc.php +++ b/modules-available/serversetup-bwlp/api.inc.php @@ -13,10 +13,9 @@ $product = Request::any('product', false, 'string'); $slxExtensions = Request::any('slx-extensions', false, 'int'); if ($platform === false || ($uuid === false && $product === false) || $slxExtensions === false) { - error_log(print_r($_SERVER, true)); - sleep(1); + // Redirect to self with added parameters $url = parse_url($_SERVER['REQUEST_URI']); - if (isset($_SERVER['SCRIPT_URI']) && preg_match('#(\w+://[^/]+)#', $_SERVER['SCRIPT_URI'], $out)) { + if (isset($_SERVER['SCRIPT_URI']) && preg_match('#^(\w+://[^/]+)#', $_SERVER['SCRIPT_URI'], $out)) { $urlbase = $out[1]; } elseif (isset($_SERVER['REQUEST_SCHEME']) && isset($_SERVER['SERVER_NAME'])) { $urlbase = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME']; @@ -62,11 +61,7 @@ HERE; } $platform = strtoupper($platform); -$BOOT_METHODS = [ - 'EXIT' => 'exit 1', - 'COMBOOT' => 'chain /tftp/chain.c32 hd0', - 'SANBOOT' => 'sanboot --no-describe', -]; +$BOOT_METHODS = Localboot::BOOT_METHODS; $ip = $_SERVER['REMOTE_ADDR']; if (substr($ip, 0, 7) === '::ffff:') { @@ -111,15 +106,9 @@ if ($model !== false) { } } if ($localboot === false || !isset($BOOT_METHODS[$localboot])) { - $localboot = Property::get('serversetup.localboot', false); - if ($localboot === false) { - if ($platform === 'EFI') { - // It seems most (all) EFI platforms won't enumerate any drives in ipxe. - // No idea if this can be fixed in ipxe code in the future. - $localboot = 'EXIT'; - } else { - $localboot = 'SANBOOT'; - } + $localboot = Property::get('serversetup.localboot', 'AUTO'); + if (!isset($BOOT_METHODS[$localboot])) { + $localboot = 'AUTO'; } } if (isset($BOOT_METHODS[$localboot])) { diff --git a/modules-available/serversetup-bwlp/inc/localboot.inc.php b/modules-available/serversetup-bwlp/inc/localboot.inc.php new file mode 100644 index 00000000..a91d0547 --- /dev/null +++ b/modules-available/serversetup-bwlp/inc/localboot.inc.php @@ -0,0 +1,17 @@ + 'iseq EFI ${platform} && exit 1 || sanboot --no-describe', + 'EXIT' => 'exit 1', + 'COMBOOT' => 'chain /tftp/chain.c32 hd0', + 'SANBOOT' => 'sanboot --no-describe', + ]; + + + +} \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/lang/de/messages.json b/modules-available/serversetup-bwlp/lang/de/messages.json index 2bcaa391..de48ef0b 100644 --- a/modules-available/serversetup-bwlp/lang/de/messages.json +++ b/modules-available/serversetup-bwlp/lang/de/messages.json @@ -7,6 +7,8 @@ "invalid-boot-entry": "Ung\u00fcltiger Booteintrag: {{0}}", "invalid-ip": "Kein Interface ist auf die Adresse {{0}} konfiguriert", "invalid-menu-id": "Ung\u00fcltige Men\u00fc-ID: {{0}}", + "localboot-invalid-method": "Ung\u00fcltige localboot-Methode: {{0}}", + "localboot-saved": "Einstellungen gespeichert", "location-menu-assigned": "{{0}} wurde ein Men\u00fc zugewiesen", "location-use-default": "{{0}} verwendet jetzt das Standardmen\u00fc", "menu-deleted": "Men\u00fc gel\u00f6scht", diff --git a/modules-available/serversetup-bwlp/lang/de/module.json b/modules-available/serversetup-bwlp/lang/de/module.json index e4c1ff4e..31d563f0 100644 --- a/modules-available/serversetup-bwlp/lang/de/module.json +++ b/modules-available/serversetup-bwlp/lang/de/module.json @@ -14,5 +14,6 @@ "submenu_address": "Server-Adresse", "submenu_bootentry": "Booteintr\u00e4ge verwalten", "submenu_download": "Downloads", + "submenu_localboot": "HDD-Boot", "submenu_menu": "Men\u00fcs verwalten" } \ No newline at end of file diff --git a/modules-available/serversetup-bwlp/lang/de/template-tags.json b/modules-available/serversetup-bwlp/lang/de/template-tags.json index 9d64ebd9..2b68b3fb 100644 --- a/modules-available/serversetup-bwlp/lang/de/template-tags.json +++ b/modules-available/serversetup-bwlp/lang/de/template-tags.json @@ -19,6 +19,7 @@ "lang_bootentryTitle": "Booteintrag", "lang_chooseIP": "Bitte w\u00e4hlen Sie die IP-Adresse, \u00fcber die der Server von den Clients zum Booten angesprochen werden soll.", "lang_commandLine": "Command line", + "lang_count": "Anzahl", "lang_customEntry": "Eigener Eintrag", "lang_downloadBootImage": "Boot-Image herunterladen", "lang_downloadRufus": "Rufus herunterladen", @@ -41,6 +42,10 @@ "lang_initRd": "Zu ladendes initramfs", "lang_isDefault": "Standard", "lang_listOfMenus": "Men\u00fcliste", + "lang_localBootDefault": "Standardm\u00e4\u00dfig verwendete Methode, um von Festplatte zu booten", + "lang_localBootExceptions": "Ausnahmen, pro Rechnermodell definierbar", + "lang_localBootHead": "Boot von Festplatte", + "lang_localBootIntro": "Aus dem iPXE Bootmen\u00fc kann auf verschiedene Arten ein Boot von der prim\u00e4ren Festplatte ausgel\u00f6st werden. In den allermeisten F\u00e4llen ist die Einstellung \"AUTO\" ausreichend, bei bestimmten Rechnermodellen kann es allerdings erforderlich sein, eine der alternativen Methoden zu erzwingen. Falls Sie einem solchen Modell begegnen, k\u00f6nnen Sie im unteren Teil dieser Seite eine solche Ausnahme festlegen. In einigen F\u00e4llen l\u00e4sst sich das Problem auch durch ein BIOS-Update auf den entsprechenden Ger\u00e4ten beheben.", "lang_localHDD": "Lokale HDD", "lang_locationCount": "Anzahl Orte", "lang_masterPassword": "Master-Passwort", @@ -60,6 +65,7 @@ "lang_newBootEntryHead": "Neuer Booteintrag", "lang_newMenu": "Neues Men\u00fc", "lang_none": "(keine)", + "lang_override": "\u00dcberschreiben", "lang_pxeBuilt": "PXE-Binary gebaut", "lang_recompileHint": "iPXE-Binaries jetzt neu kompilieren. Normalerweise wird dieser Vorgang bei \u00c4nderungen automatisch ausgef\u00fchrt. Sollten Bootprobleme auftreten, k\u00f6nnen Sie hier den Vorgang manuell ansto\u00dfen.", "lang_scriptContent": "Script", diff --git a/modules-available/serversetup-bwlp/page.inc.php b/modules-available/serversetup-bwlp/page.inc.php index a71e56ef..7766050b 100644 --- a/modules-available/serversetup-bwlp/page.inc.php +++ b/modules-available/serversetup-bwlp/page.inc.php @@ -88,6 +88,11 @@ class Page_ServerSetup extends Page Util::redirect('?do=locations'); } + if ($action === 'savelocalboot') { + User::assertPermission('ipxe.localboot.edit'); + $this->saveLocalboot(); + } + if ($action === 'deleteMenu') { // Permcheck in function $this->deleteMenu(); @@ -114,6 +119,9 @@ class Page_ServerSetup extends Page if (User::hasPermission('download')) { Dashboard::addSubmenu('?do=serversetup&show=download', Dictionary::translate('submenu_download', true)); } + if (User::hasPermission('ipxe.localboot.*')) { + Dashboard::addSubmenu('?do=serversetup&show=localboot', Dictionary::translate('submenu_localboot', true)); + } if (Request::get('show') === false) { $subs = Dashboard::getSubmenus(); if (empty($subs)) { @@ -168,6 +176,10 @@ class Page_ServerSetup extends Page // Permcheck in function $this->showEditLocation(); break; + case 'localboot': + User::assertPermission('ipxe.localboot.*'); + $this->showLocalbootConfig(); + break; default: Util::redirect('?do=serversetup'); break; @@ -218,6 +230,49 @@ class Page_ServerSetup extends Page Render::addTemplate('download', ['files' => $files]); } + private function makeSelectArray($list, $default) + { + $ret = []; + foreach (array_keys($list) as $k) { + $ret[] = [ + 'key' => $k, + 'selected' => ($k === $default ? 'selected' : ''), + ]; + } + return $ret; + } + + private function showLocalbootConfig() + { + // Default setting + $default = Property::get('serversetup.localboot', false); + if (!array_key_exists($default, Localboot::BOOT_METHODS)) { + $default = 'AUTO'; + } + $optionList = $this->makeSelectArray(Localboot::BOOT_METHODS, $default); + // Exceptions + $cutoff = strtotime('-90 days'); + $models = []; + $res = Database::simpleQuery('SELECT m.systemmodel, cnt, sl.bootmethod FROM ( + SELECT m2.systemmodel, Count(*) AS cnt FROM machine m2 + WHERE m2.lastseen > :cutoff + GROUP BY systemmodel + ) m + LEFT JOIN serversetup_localboot sl USING (systemmodel) + ORDER BY systemmodel', ['cutoff' => $cutoff]); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['options'] = $this->makeSelectArray(Localboot::BOOT_METHODS, $row['bootmethod']); + $models[] = $row; + } + // Output + $data = [ + 'default' => $default, + 'options' => $optionList, + 'exceptions' => $models, + ]; + Render::addTemplate('localboot', $data); + } + private function showBootentryList() { $allowEdit = User::hasPermission('ipxe.bootentry.edit'); @@ -734,4 +789,27 @@ class Page_ServerSetup extends Page Message::addSuccess('location-menu-assigned', $loc['locationname']); } + private function saveLocalboot() + { + $default = Request::post('default', 'AUTO', 'string'); + if (!array_key_exists($default, Localboot::BOOT_METHODS)) { + Message::addError('localboot-invalid-method', $default); + return; + } + $overrides = Request::post('override', [], 'array'); + Database::exec('TRUNCATE TABLE serversetup_localboot'); + foreach ($overrides as $model => $mode) { + if (empty($mode)) // No override + continue; + if (!array_key_exists($mode, Localboot::BOOT_METHODS)) { + Message::addWarning('localboot-invalid-method', $mode); + continue; + } + Database::exec('INSERT INTO serversetup_localboot (systemmodel, bootmethod) + VALUES (:model, :mode)', compact('model', 'mode')); + } + Message::addSuccess('localboot-saved'); + Util::redirect('?do=serversetup&show=localboot'); + } + } diff --git a/modules-available/serversetup-bwlp/templates/localboot.html b/modules-available/serversetup-bwlp/templates/localboot.html new file mode 100644 index 00000000..7000be37 --- /dev/null +++ b/modules-available/serversetup-bwlp/templates/localboot.html @@ -0,0 +1,59 @@ +

{{lang_localBootHead}}

+ +

{{lang_localBootIntro}}

+ +
+ + + + +
+
+ + +
+ +
+

+ {{lang_localBootExceptions}} +

+ + + + + + + {{#exceptions}} + + + + + + {{/exceptions}} +
{{lang_name}}{{lang_count}}{{lang_override}}
{{systemmodel}}{{cnt}} + +
+ +
+ + +
+ +
-- cgit v1.2.3-55-g7522 From b0e0eddcfeb0f7d7cdde6caad21e3a0485797890 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 18 Jan 2019 16:49:46 +0100 Subject: [serversetup-bwlp] Link systemmodel to machine list --- modules-available/serversetup-bwlp/templates/localboot.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules-available') diff --git a/modules-available/serversetup-bwlp/templates/localboot.html b/modules-available/serversetup-bwlp/templates/localboot.html index 7000be37..960f463d 100644 --- a/modules-available/serversetup-bwlp/templates/localboot.html +++ b/modules-available/serversetup-bwlp/templates/localboot.html @@ -31,7 +31,7 @@ {{#exceptions}} - {{systemmodel}} + {{systemmodel}} {{cnt}} - +
@@ -121,6 +121,13 @@
{{/builtin}} +

{{lang_referencingMenus}}:

+ +