@@ -665,10 +665,29 @@ function bootParamsTabFocus() {
}
// Update configuration preview
-function updateConfig() {
+function ensureApplyTogglesEnabled(labels) {
+ if (!Array.isArray(labels) || labels.length === 0) {
+ return;
+ }
+
+ $('.boot-entry-apply-toggle').each(function() {
+ const toggle = $(this);
+ const label = toggle.attr('data-label');
+
+ if (labels.indexOf(label) !== -1 && !toggle.prop('checked')) {
+ toggle.switchButton('option', 'checked', true);
+ }
+ });
+}
+
+function updateConfig(changedFieldId) {
// Handle mutual exclusivity for sysfb_blacklist toggle
handleFramebufferMutualExclusivity();
+ if (changedFieldId === 'acs-dropdown' || changedFieldId === 'vfio-toggle') {
+ ensureApplyTogglesEnabled(['Unraid OS', 'Unraid OS GUI Mode']);
+ }
+
markFormModified();
updatePreview();
}
@@ -1044,7 +1063,7 @@ function updatePreview() {
if (bootloaderType === 'grub') {
html = 'linux /bzimage ';
} else {
- html = 'append initrd=/bzroot ';
+ html = 'append ' + getPreviewSyslinuxInitrd() + ' ';
}
// Add all proposed parameters in bright green with bold weight for visibility
@@ -1203,11 +1222,47 @@ function stripSystemParams(line) {
return indent + rest;
}
+function normalizeDisplayLineForComparison(line) {
+ if (!line) return '';
+ return stripSystemParams(line).trim().replace(/\s+/g, ' ');
+}
+
+function getSyslinuxInitrdForEntryLabel(label) {
+ return /GUI Mode|GUI Safe Mode/i.test(label || '') ? 'initrd=/bzroot,/bzroot-gui' : 'initrd=/bzroot';
+}
+
+function getPreviewSyslinuxInitrd() {
+ const appliedLabels = [];
+ $('.boot-entry-apply-toggle:checked').each(function() {
+ appliedLabels.push($(this).attr('data-label') || '');
+ });
+
+ if (appliedLabels.length > 0 && appliedLabels.every(function(label) {
+ return /GUI Mode|GUI Safe Mode/i.test(label);
+ })) {
+ return 'initrd=/bzroot,/bzroot-gui';
+ }
+
+ return 'initrd=/bzroot';
+}
+
+function getSelectedDefaultEntryLabel() {
+ const selected = $('input[name="default-boot"]:checked').attr('data-label');
+ return selected || '';
+}
+
/**
* Helper function to create boot entry content with optional diff display
*/
function createEntryContent(entry, showApply) {
const content = $('').addClass('boot-entry-content');
+ const selectedDefaultLabel = getSelectedDefaultEntryLabel();
+ const shouldShowMenuDefault = bootloaderType === 'syslinux'
+ ? (selectedDefaultLabel ? entry.label === selectedDefaultLabel : entry.isDefault)
+ : entry.isDefault;
+ const originalHasMenuDefault = bootloaderType === 'syslinux' && entry.lines.some(function(entryLine) {
+ return entryLine.trim() === 'menu default';
+ });
// Find the line index to update (append for syslinux, linux for GRUB)
let appendLineIndex = -1;
@@ -1227,6 +1282,25 @@ function createEntryContent(entry, showApply) {
// Build the content
let contentHtml = '';
entry.lines.forEach((line, idx) => {
+ const trimmed = line.trim();
+
+ if (bootloaderType === 'syslinux' && trimmed.startsWith('label ')) {
+ contentHtml += stripSystemParams(line) + '\n';
+ if (shouldShowMenuDefault && !originalHasMenuDefault) {
+ contentHtml += ' menu default\n';
+ }
+ return;
+ }
+
+ if (bootloaderType === 'syslinux' && trimmed === 'menu default') {
+ if (shouldShowMenuDefault) {
+ contentHtml += stripSystemParams(line) + '\n';
+ } else {
+ contentHtml += '' + stripSystemParams(line) + '\n';
+ }
+ return;
+ }
+
if (idx === appendLineIndex && showApply) {
// Show diff: removed line (original) and added line (with parameters)
const originalAppend = stripSystemParams(line);
@@ -1257,10 +1331,12 @@ function createEntryContent(entry, showApply) {
const args = mergedArgs.length > 0 ? ' ' + mergedArgs.join(' ') : '';
newAppend = indent + cmd + ' ' + kernel + args;
} else {
- newAppend = ' append initrd=/bzroot' + (proposedParams.length > 0 ? ' ' + proposedParams.join(' ') : '');
+ const indentMatch = line.match(/^(\s*)append\b/);
+ const indent = indentMatch ? indentMatch[1] : ' ';
+ newAppend = indent + 'append ' + getSyslinuxInitrdForEntryLabel(entry.label) + (proposedParams.length > 0 ? ' ' + proposedParams.join(' ') : '');
}
const newAppendDisplay = stripSystemParams(newAppend);
- if (originalAppend === newAppendDisplay) {
+ if (normalizeDisplayLineForComparison(originalAppend) === normalizeDisplayLineForComparison(newAppendDisplay)) {
contentHtml += originalAppend + '\n';
} else {
contentHtml += '' + originalAppend + '\n';
@@ -1302,6 +1378,28 @@ function updateAllAppliedEntryDiffs() {
});
}
+function updateDefaultEntryDisplay() {
+ const selectedDefaultLabel = getSelectedDefaultEntryLabel();
+
+ $('.boot-entry').each(function() {
+ const entryDiv = $(this);
+ const entry = entryDiv.data('entry');
+ if (!entry) return;
+
+ const toggle = entryDiv.find('.boot-entry-apply-toggle');
+ if (toggle.length > 0 && selectedDefaultLabel && entry.label === selectedDefaultLabel && !toggle.prop('checked')) {
+ toggle.switchButton('option', 'checked', true);
+ }
+
+ entryDiv.toggleClass('default', !!selectedDefaultLabel && entry.label === selectedDefaultLabel);
+
+ const showApply = toggle.length > 0 && toggle.prop('checked');
+ updateEntryDiff(entryDiv, entry, showApply);
+ });
+
+ updatePreview();
+}
+
/**
* Render menu view from parsed configuration
*/
@@ -1443,6 +1541,7 @@ function renderMenuView(parsed) {
.prop('checked', entry.isDefault)
.on('change', function() {
markFormModified();
+ updateDefaultEntryDisplay();
});
header.append(radio);
}
diff --git a/emhttp/plugins/dynamix/scripts/manage_boot_params.sh b/emhttp/plugins/dynamix/scripts/manage_boot_params.sh
index c6aeaf9f7..a64582a0f 100755
--- a/emhttp/plugins/dynamix/scripts/manage_boot_params.sh
+++ b/emhttp/plugins/dynamix/scripts/manage_boot_params.sh
@@ -39,6 +39,14 @@ detect_bootloader() {
BOOTLOADER_TYPE="${BOOTLOADER_TYPE:-$(detect_bootloader)}"
+normalize_file_to_crlf() {
+ local file_path="$1"
+ local normalized_file="${file_path}.crlf"
+
+ awk '{ sub(/\r$/, ""); printf "%s\r\n", $0 }' "$file_path" > "$normalized_file"
+ mv "$normalized_file" "$file_path"
+}
+
#############################################
# JSON Escaping Function
#############################################
@@ -265,17 +273,26 @@ parse_append_line() {
# Matches from "label $label" to the next "label" or end of file
# Normalize CRLF to LF for parsing (DOS-edited syslinux.cfg)
tr -d '\r' < "$cfg_file" | awk -v label="$label" '
- BEGIN { label=tolower(label) }
- /^label / {
+ BEGIN {
+ label=tolower(label)
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", label)
+ gsub(/[[:space:]]+/, " ", label)
+ }
+ {
+ sub(/\r$/, "")
+ }
+ /^label[[:space:]]+/ {
line=tolower($0)
- if (line ~ "^label " label "$") {
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
+ gsub(/[[:space:]]+/, " ", line)
+ if (line ~ "^label[[:space:]]+" label "$") {
in_section=1
} else {
in_section=0
}
}
- in_section && /^ append/ {
- sub(/^ append /, "")
+ in_section && /^[[:space:]]*append([[:space:]]|$)/ {
+ sub(/^[[:space:]]*append[[:space:]]+/, "")
print
exit
}
@@ -662,7 +679,7 @@ build_append_line() {
# 4. Power Management (usbcore.autosuspend, nvme_core, pcie_aspm, pcie_port_pm)
# 5. Custom Parameters (in array order, excluding pci=)
build_append_line_gui_safe() {
- local params="initrd=/bzroot"
+ local params="initrd=/bzroot,/bzroot-gui"
# 1. VM Passthrough (safe for GUI mode)
[[ -n "${ACS_OVERRIDE}" ]] && params="$params pcie_acs_override=${ACS_OVERRIDE}"
@@ -735,7 +752,7 @@ build_kernel_args() {
}
build_kernel_args_gui_safe() {
- echo "$(build_append_line_gui_safe | sed 's/^initrd=\/bzroot[ ]*//')"
+ echo "$(build_append_line_gui_safe | sed 's/^initrd=\/bzroot,\/bzroot-gui[ ]*//')"
}
#############################################
@@ -794,32 +811,74 @@ update_default_boot() {
awk '!/^ menu default$/' "$temp_file" > "${temp_file}.default"
mv "${temp_file}.default" "$temp_file"
- # Now add "menu default" to the target label
+ # Now add "menu default" immediately after the target label
awk -v label="$target_label" '
/^label / {
current_label = $0
sub(/^label /, "", current_label)
- in_target = (current_label == label)
+ print
+ if (current_label == label) {
+ print " menu default"
+ }
+ next
}
{
print
- # After printing the label line, check if we need to add menu default
- if (in_target && /^label /) {
- # Check if next line exists and is not menu default
- getline next_line
- if (next_line !~ /^ menu default$/) {
- print " menu default"
- print next_line
- } else {
- print next_line
- }
- in_target = 0
- }
}
' "$temp_file" > "${temp_file}.default"
mv "${temp_file}.default" "$temp_file"
}
+update_syslinux_label_append() {
+ local target_label="$1"
+ local append_args="$2"
+ local cfg_file="$3"
+
+ awk -v label="$target_label" -v append_args="$append_args" '
+ BEGIN {
+ normalized_label = label
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", normalized_label)
+ gsub(/[[:space:]]+/, " ", normalized_label)
+ }
+ /^[[:space:]]*label[[:space:]]+/ {
+ if (in_section && !replaced) {
+ print section_indent "append " append_args
+ }
+
+ current_label = $0
+ sub(/^[[:space:]]*label[[:space:]]+/, "", current_label)
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", current_label)
+ gsub(/[[:space:]]+/, " ", current_label)
+ in_section = (current_label == normalized_label)
+ replaced = 0
+ section_indent = " "
+ }
+ {
+ if (in_section && $0 ~ /^[[:space:]]*append([[:space:]]|$)/) {
+ if (!replaced) {
+ match($0, /^[[:space:]]*/)
+ section_indent = substr($0, RSTART, RLENGTH)
+ if (section_indent == "") {
+ section_indent = " "
+ }
+ print section_indent "append " append_args
+ replaced = 1
+ }
+ next
+ }
+
+ print
+ }
+ END {
+ if (in_section && !replaced) {
+ print section_indent "append " append_args
+ }
+ }
+ ' "$cfg_file" > "${cfg_file}.append"
+
+ mv "${cfg_file}.append" "$cfg_file"
+}
+
#############################################
# Write new configuration to syslinux.cfg
#############################################
@@ -863,100 +922,46 @@ write_config() {
local temp_file=$(mktemp -p "$(dirname "$SYSLINUX_CFG")")
cp "$SYSLINUX_CFG" "$temp_file"
+ # Strip CR from CRLF so awk/grep/sed-based syslinux updates match Unix line endings.
+ local temp_lf="${temp_file}.lf"
+ tr -d '\r' < "$temp_file" > "$temp_lf" && mv "$temp_lf" "$temp_file"
+
# Update "Unraid OS" label if enabled in UI
if [[ "${APPLY_TO_UNRAID_OS}" == "1" ]]; then
- awk -v new_append=" append $new_append" '
- /^label Unraid OS$/ {
- in_section=1
- }
- /^label / && !/^label Unraid OS$/ {
- in_section=0
- }
- {
- if (in_section && /^ append/) {
- print new_append
- } else {
- print
- }
- }
- ' "$temp_file" > "${temp_file}.1"
- mv "${temp_file}.1" "$temp_file"
+ update_syslinux_label_append "Unraid OS" "$new_append" "$temp_file"
fi
# Update "Unraid OS GUI Mode" label if enabled in UI
if [[ "${APPLY_TO_GUI_MODE}" == "1" ]]; then
# Optionally exclude framebuffer parameters for GUI mode entries
+ local gui_append
if [[ "${EXCLUDE_FRAMEBUFFER_FROM_GUI}" == "1" ]]; then
- local gui_append=" append $(build_append_line_gui_safe)"
+ gui_append="$(build_append_line_gui_safe)"
else
- local gui_append=" append $new_append"
+ gui_append="$(echo "$new_append" | sed 's/^initrd=\/bzroot\([ ]\|$\)/initrd=\/bzroot,\/bzroot-gui\1/')"
fi
- awk -v new_append="$gui_append" '
- /^label Unraid OS GUI Mode$/ {
- in_section=1
- }
- /^label / && !/^label Unraid OS GUI Mode$/ {
- in_section=0
- }
- {
- if (in_section && /^ append/) {
- print new_append
- } else {
- print
- }
- }
- ' "$temp_file" > "${temp_file}.2"
- mv "${temp_file}.2" "$temp_file"
+ update_syslinux_label_append "Unraid OS GUI Mode" "$gui_append" "$temp_file"
fi
# Update "Unraid OS Safe Mode" label if enabled in UI
if [[ "${APPLY_TO_SAFE_MODE}" == "1" ]]; then
- awk -v new_append=" append $new_append" '
- /^label Unraid OS Safe Mode/ {
- in_section=1
- }
- /^label / && !/^label Unraid OS Safe Mode/ {
- in_section=0
- }
- {
- if (in_section && /^ append/) {
- print new_append
- } else {
- print
- }
- }
- ' "$temp_file" > "${temp_file}.3"
- mv "${temp_file}.3" "$temp_file"
+ update_syslinux_label_append "Unraid OS Safe Mode (no plugins, no GUI)" "$new_append" "$temp_file"
fi
# Update "Unraid OS GUI Safe Mode" label if enabled in UI
if [[ "${APPLY_TO_GUI_SAFE_MODE}" == "1" ]]; then
# Optionally exclude framebuffer parameters for GUI mode entries
+ local gui_safe_append
if [[ "${EXCLUDE_FRAMEBUFFER_FROM_GUI}" == "1" ]]; then
- local gui_safe_append=" append $(build_append_line_gui_safe)"
+ gui_safe_append="$(build_append_line_gui_safe)"
else
- local gui_safe_append=" append $new_append"
+ gui_safe_append="$(echo "$new_append" | sed 's/^initrd=\/bzroot\([ ]\|$\)/initrd=\/bzroot,\/bzroot-gui\1/')"
fi
- awk -v new_append="$gui_safe_append" '
- /^label Unraid OS GUI Safe Mode/ {
- in_section=1
- }
- /^label / && !/^label Unraid OS GUI Safe Mode/ {
- in_section=0
- }
- {
- if (in_section && /^ append/) {
- print new_append
- } else {
- print
- }
- }
- ' "$temp_file" > "${temp_file}.4"
- mv "${temp_file}.4" "$temp_file"
+ update_syslinux_label_append "Unraid OS GUI Safe Mode (no plugins)" "$gui_safe_append" "$temp_file"
fi
# Update default boot entry if requested
@@ -970,6 +975,8 @@ write_config() {
rm -f "${temp_file}.timeout"
fi
+ normalize_file_to_crlf "$temp_file"
+
# Validate the modified config
if validate_config "$temp_file"; then
# Move temp file to actual config
@@ -1141,6 +1148,8 @@ write_config_grub() {
fi
fi
+ normalize_file_to_crlf "$temp_file"
+
# Basic validation: ensure the config is non-empty and has at least one menuentry
if [[ ! -s "$temp_file" ]] || ! grep -q '^menuentry ' "$temp_file"; then
rm -f "$temp_file"
@@ -1192,6 +1201,8 @@ write_raw_config() {
# Write raw config to temp file
echo "$RAW_CONFIG" > "$temp_file"
+ normalize_file_to_crlf "$temp_file"
+
# Validate the raw config (syslinux only)
if [[ "$BOOTLOADER_TYPE" == "grub" ]] || validate_config "$temp_file"; then
# Move temp file to actual config
diff --git a/emhttp/plugins/dynamix/scripts/system_information b/emhttp/plugins/dynamix/scripts/system_information
index ed91c8f18..2447f72b7 100755
--- a/emhttp/plugins/dynamix/scripts/system_information
+++ b/emhttp/plugins/dynamix/scripts/system_information
@@ -28,6 +28,7 @@ function port_get_contents($port) {
$var = (array)@parse_ini_file('state/var.ini');
$model = _var($var,'SYS_MODEL',_('N/A'));
$board = dmidecode('Base Board Information',2,0);
+$chassis = dmidecode('Chassis Information',3,0);
$bios = dmidecode('BIOS Information',0,0);
$cpu = dmidecode('Processor Information',4,0);
$cpumodel = str_ireplace(["Processor","(C)","(R)","(TM)"],["","©","®","™"],exec("grep -Pom1 '^model name\s+:\s*\K.+' /proc/cpuinfo") ?: $cpu['Version']);
@@ -38,6 +39,7 @@ $board['Manufacturer'] = (empty($board['Manufacturer'])) ? _('Unknown') : $boa
$board['Product Name'] = (empty($board['Product Name'])) ? "" : $board['Product Name'];
$board['Version'] = (empty($board['Version'])) ? "" : _('Version')." ".$board['Version'];
$board['Serial Number'] = (empty($board['Serial Number'])) ? "" : _('s/n')." ".$board['Serial Number'];
+$chassis['Serial Number'] = (empty($chassis['Serial Number'])) ? "" : _('s/n')." ".$chassis['Serial Number'];
$bios['Vendor'] = (empty($bios['Vendor'])) ? "" : $bios['Vendor'];
$bios['Version'] = (empty($bios['Version'])) ? "" : _('Version')." ".$bios['Version'];
$bios['Release Date'] = (empty($bios['Release Date'])) ? "" : _('Dated')." ".$bios['Release Date'];
@@ -162,6 +164,9 @@ span.link{text-decoration:underline;cursor:pointer}
| =_('Model')?>: | =htmlspecialchars($model)?> |
| =('M/B')?>: | ="{$board['Manufacturer']} {$board['Product Name']} {$board['Version']} {$board['Serial Number']}"?> |
+
+| =_('Chassis')?>: | =htmlspecialchars($chassis['Serial Number'], ENT_QUOTES, 'UTF-8')?> |
+
| =_('BIOS')?>: | ="{$bios['Vendor']} {$bios['Version']} {$bios['Release Date']}"?> |
| =_('CPU')?>: | ="$cpumodel {$cpu['Current Speed']}"?> |
| =_('HVM')?>: | =$hvm?> |