Files
ericbsd 4d4c54671e Rework ZFS page UI layout and swap sizing
Redesign the ZFS configuration page with a two-column layout (settings
on the left, disk list on the right), add a user-editable swap size
field that defaults to actual RAM size, make the pool name always
editable, and simplify pool type values to plain identifiers (stripe,
mirror, raidz1/2/3). Consolidate duplicated next-button sensitivity
logic into _update_next_button(), replace deprecated Gtk.STOCK icons
with icon names, encrypt swap when GELI is enabled, and bump version
to 0.4.
2026-05-13 08:11:34 -03:00

331 lines
11 KiB
Python

#!/usr/bin/env python
import re
import os
from subprocess import Popen, run, PIPE
from install_station.data import pc_sysinstall
def replace_pattern(current: str, new: str, file: str) -> None:
"""Replace text patterns in a file using regex substitution.
Args:
current: Regular expression pattern to search for
new: Replacement text
file: Path to file to modify
"""
parser_file = open(file, 'r').read()
parser_patched = re.sub(current, new, parser_file)
save_parser_file = open(file, 'w')
save_parser_file.writelines(parser_patched)
save_parser_file.close()
def language_dictionary() -> dict[str, str]:
"""Get available system languages from pc-sysinstall.
Returns:
Dictionary mapping language names to language codes
"""
langs = Popen(f'{pc_sysinstall} query-langs', shell=True, stdin=PIPE,
stdout=PIPE, universal_newlines=True,
close_fds=True).stdout.readlines()
dictionary = {}
for line in langs:
lang_list = line.rstrip()
lang_name = lang_list.partition(' ')[2]
lang_code = lang_list.partition(' ')[0]
dictionary[lang_name] = lang_code
return dictionary
def localize_system(locale: str) -> None:
"""Apply localization settings to the system.
Updates login.conf, profile files, and greeter configurations
with the specified locale.
Args:
locale: Language code (e.g. 'en_US', 'fr_FR')
"""
slick_greeter = "/usr/local/share/xgreeters/slick-greeter.desktop"
gtk_greeter = "/usr/local/share/xgreeters/lightdm-gtk-greeter.desktop"
replace_pattern('lang=C', f'lang={locale}', '/etc/login.conf')
replace_pattern('en_US', locale, '/etc/profile')
replace_pattern('en_US', locale, '/usr/share/skel/dot.profile')
if os.path.exists(slick_greeter):
replace_pattern(
'Exec=slick-greeter',
f'Exec=env LANG={locale}.UTF-8 slick-greeter',
slick_greeter
)
elif os.path.exists(gtk_greeter):
replace_pattern(
'Exec=lightdm-gtk-greete',
f'Exec=env LANG={locale}.UTF-8 lightdm-gtk-greeter',
gtk_greeter
)
def keyboard_dictionary() -> dict[str, dict[str, str | None]]:
"""Get available keyboard layouts and variants from pc-sysinstall.
Returns:
Dictionary mapping keyboard layout names to layout/variant dictionaries
"""
xkeyboard_layouts = Popen(f'{pc_sysinstall} xkeyboard-layouts', shell=True,
stdout=PIPE,
universal_newlines=True).stdout.readlines()
dictionary = {}
for line in xkeyboard_layouts:
keyboard_list = list(filter(None, line.rstrip().split(' ')))
kb_name = keyboard_list[1].strip()
kb_layouts = keyboard_list[0].strip()
kb_variant = None
# Skip the "custom" layout as it's not a real keyboard layout
if kb_layouts != 'custom':
dictionary[kb_name] = {'layout': kb_layouts, 'variant': kb_variant}
xkeyboard_variants = Popen(f'{pc_sysinstall} xkeyboard-variants',
shell=True, stdout=PIPE,
universal_newlines=True).stdout.readlines()
for line in xkeyboard_variants:
xkb_variant = line.rstrip()
kb_name = xkb_variant.partition(':')[2].strip()
keyboard_list = list(filter
(None, xkb_variant.partition(':')[0].split()))
kb_layouts = keyboard_list[1].strip()
kb_variant = keyboard_list[0].strip()
dictionary[kb_name] = {'layout': kb_layouts, 'variant': kb_variant}
return dictionary
def keyboard_models() -> dict[str, str]:
"""Get available keyboard models from pc-sysinstall.
Returns:
Dictionary mapping keyboard model names to model codes
"""
xkeyboard_models = Popen(f'{pc_sysinstall} xkeyboard-models', shell=True,
stdout=PIPE,
universal_newlines=True).stdout.readlines()
dictionary = {}
for line in xkeyboard_models:
kbm_name = line.rstrip().partition(' ')[2]
kbm_code = line.rstrip().partition(' ')[0]
dictionary[kbm_name] = kbm_code
return dictionary
def change_keyboard(kb_layout: str, kb_variant: str | None = None, kb_model: str | None = None) -> None:
"""Apply keyboard layout change immediately using setxkbmap.
Args:
kb_layout: Keyboard layout code
kb_variant: Optional keyboard variant code
kb_model: Optional keyboard model code
"""
if kb_variant is None and kb_model is not None:
run(f"setxkbmap -layout {kb_layout} -model {kb_model}", shell=True)
elif kb_variant is not None and kb_model is None:
run(f"setxkbmap -layout {kb_layout} -variant {kb_variant}", shell=True)
elif kb_variant is not None and kb_model is not None:
set_kb_cmd = f"setxkbmap -layout {kb_layout} -variant {kb_variant} " \
f"-model {kb_model}"
run(set_kb_cmd, shell=True)
else:
run(f"setxkbmap -layout {kb_layout}", shell=True)
def set_keyboard(kb_layout: str, kb_variant: str | None = None, kb_model: str | None = None) -> None:
"""
Permanently configure keyboard layout for the live system.
Based on pc-sysinstall's localize_x_keyboard function.
"""
setxkbmap_cmd = ""
# Build setxkbmap command
if kb_model and kb_model != "NONE":
setxkbmap_cmd = f"-model {kb_model}"
kx_model = kb_model
else:
kx_model = "pc104"
if kb_layout and kb_layout != "NONE":
setxkbmap_cmd = f"{setxkbmap_cmd} -layout {kb_layout}".strip()
kx_layout = kb_layout
else:
kx_layout = "us"
if kb_variant and kb_variant != "NONE":
setxkbmap_cmd = f"{setxkbmap_cmd} -variant {kb_variant}"
# Apply the keyboard layout immediately
if setxkbmap_cmd:
run(f"setxkbmap {setxkbmap_cmd}", shell=True)
# Create .xprofile for persistent keyboard layout
xprofile_path = "/home/ghostbsd/.xprofile"
try:
# Read existing .xprofile or create new one
if os.path.exists(xprofile_path):
with open(xprofile_path, 'r') as f:
content = f.read()
# Remove existing setxkbmap lines
lines = [line for line in content.splitlines() if not line.strip().startswith('setxkbmap')]
else:
lines = ["#!/bin/sh"]
# Add new setxkbmap command
lines.append(f"setxkbmap {setxkbmap_cmd}")
# Write back to .xprofile
with open(xprofile_path, 'w') as f:
f.write('\n'.join(lines) + '\n')
# Make executable
os.chmod(xprofile_path, 0o755)
except (OSError, IOError) as e:
print(f"Warning: Could not update .xprofile: {e}")
# Set console keymap in rc.conf for live system persistence
try:
_set_console_keymap(kx_layout)
except (OSError, IOError) as e:
print(f"Warning: Could not update console keymap: {e}")
def _set_console_keymap(key_layout: str) -> None:
"""Helper function to set console keymap in rc.conf"""
# Map X11 layouts to console keymaps (from pc-sysinstall)
keymap_mapping = {
'ca': 'ca-fr.kbd',
'et': 'ee.kbd',
'es': 'es.acc.kbd',
'gb': 'uk.kbd'
}
console_keymap = keymap_mapping.get(key_layout, f"{key_layout}.kbd")
rc_conf_path = "/etc/rc.conf"
keymap_line = f'keymap="{console_keymap}"\n'
# Check if keymap already exists in rc.conf
if os.path.exists(rc_conf_path):
with open(rc_conf_path, 'r') as f:
lines = f.readlines()
# Remove existing keymap lines
lines = [line for line in lines if not line.strip().startswith('keymap=')]
# Add new keymap
lines.append(keymap_line)
with open(rc_conf_path, 'w') as f:
f.writelines(lines)
else:
with open(rc_conf_path, 'w') as f:
f.write(keymap_line)
def timezone_dictionary() -> dict[str, list[str]]:
"""Get available timezones from pc-sysinstall.
Returns:
Dictionary mapping continents to lists of cities/regions
"""
tz_list = Popen(f'{pc_sysinstall} list-tzones', shell=True,
stdout=PIPE, universal_newlines=True).stdout.readlines()
city_list = []
dictionary = {}
last_continent = ''
for zone in tz_list:
zone_list = zone.partition(':')[0].rstrip().split('/')
continent = zone_list[0]
if continent != last_continent:
city_list = []
if len(zone_list) == 3:
city = zone_list[1] + '/' + zone_list[2]
elif len(zone_list) == 4:
city = zone_list[1] + '/' + zone_list[2] + '/' + zone_list[3]
else:
city = zone_list[1]
city_list.append(city)
dictionary[continent] = city_list
last_continent = continent
return dictionary
def get_ram_size_mb() -> int:
"""Query the system RAM size in megabytes using sysctl.
Returns:
System RAM size in MB
"""
output = Popen(
'sysctl -n hw.realmem',
shell=True,
stdout=PIPE,
universal_newlines=True
).stdout.read().strip()
return int(output) // 1048576
def zfs_disk_query() -> list[str]:
"""Query available disks for ZFS installation.
Returns:
List of available disk device names
"""
disk_output = Popen(
f"{pc_sysinstall} disk-list",
shell=True,
stdin=PIPE,
stdout=PIPE,
universal_newlines=True,
close_fds=True
)
return disk_output.stdout.readlines()
def zfs_disk_size_query(disk: str) -> str:
"""Query disk size information.
Args:
disk: Disk device name
Returns:
Disk size information string
"""
disk_info_output = Popen(
f"{pc_sysinstall} disk-info {disk}",
shell=True,
stdin=PIPE,
stdout=PIPE,
universal_newlines=True,
close_fds=True
)
return disk_info_output.stdout.readlines()[3].partition('=')[2]
def set_admin_user(username: str, name: str, password: str, shell: str, homedir: str, hostname: str) -> None:
"""Set up administrator user and system hostname.
Args:
username: Username for the admin user
name: Full name for the admin user
password: Password for the admin user
shell: Default shell for the admin user
homedir: Home directory path for the admin user
hostname: System hostname to set
"""
# Set Root user
run(f"echo '{password}' | pw usermod -n root -h 0", shell=True)
cmd = f"echo '{password}' | pw useradd {username} -c {name} -h 0" \
f" -s {shell} -m -d {homedir} -g wheel,operator"
run(cmd, shell=True)
run(f"sysrc hostname={hostname}", shell=True)
run(f"hostname {hostname}", shell=True)