Skip to content

Commit 3f3e3c7

Browse files
committed
DLPX-95602 X11 console Keymaps broken
PR URL: https://www.github.com/delphix/delphix-platform/pull/552
1 parent cdd6630 commit 3f3e3c7

File tree

1 file changed

+62
-4
lines changed

1 file changed

+62
-4
lines changed

files/common/usr/bin/delphix-startup-screen

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ the IP address, hostname, and services (description and status).
2222
import curses
2323
import curses.ascii
2424
import curses.textpad
25+
import re
2526
import sys
2627
import signal
2728
import os
29+
import shutil
30+
import tempfile
2831
import logging
2932
import subprocess
3033
from typing import List, Any, Tuple, Generator
@@ -102,11 +105,66 @@ def get_keyboard_layout() -> str:
102105

103106
def set_keyboard_layout(layout: str) -> subprocess.CompletedProcess:
104107
"""
105-
Set the keyboard layout based on the user selection.
108+
Set the keyboard layout by editing /etc/default/keyboard and applying it with setupcon.
109+
This avoids localectl (which is disabled on Debian/Ubuntu builds).
110+
111+
Notes:
112+
- Does NOT trigger update-initramfs; only applies to the running system's console. Delphix
113+
doesn't have TTY interactions before pivot-root, e.g. encrypted root passphrase, and
114+
rescue shell can be done with the default "us" layout.
106115
"""
107-
cmd: List[str] = ['localectl', 'set-x11-keymap', layout, 'pc105']
108-
subprocess.run(cmd, shell=False, check=True)
109-
return subprocess.run('setupcon', shell=False, check=True)
116+
kb_path = "/etc/default/keyboard"
117+
kb_backup = kb_path + ".bak"
118+
119+
# Read current content (if file doesn't exist, start with a minimal stub)
120+
try:
121+
with open(kb_path, "r", encoding="utf-8") as f:
122+
lines = f.readlines()
123+
except FileNotFoundError:
124+
lines = [
125+
'XKBMODEL="pc105"\n',
126+
f'XKBLAYOUT="{layout}"\n',
127+
'XKBVARIANT=""\n',
128+
'XKBOPTIONS=""\n',
129+
'BACKSPACE="guess"\n',
130+
]
131+
else:
132+
# Update or append XKBLAYOUT line
133+
updated = False
134+
pattern = re.compile(r'^\s*XKBLAYOUT\s*=')
135+
for i, line in enumerate(lines):
136+
if pattern.match(line):
137+
lines[i] = f'XKBLAYOUT="{layout}"\n'
138+
updated = True
139+
break
140+
if not updated:
141+
# Append if not present
142+
lines.append(f'XKBLAYOUT="{layout}"\n')
143+
144+
# Atomic write with backup
145+
os.makedirs(os.path.dirname(kb_path), exist_ok=True)
146+
if os.path.exists(kb_path):
147+
shutil.copy2(kb_path, kb_backup)
148+
149+
fd, tmp = tempfile.mkstemp(prefix=".keyboard.",
150+
dir=os.path.dirname(kb_path))
151+
try:
152+
with os.fdopen(fd, "w", encoding="utf-8") as f:
153+
f.writelines(lines)
154+
os.replace(tmp, kb_path)
155+
except Exception:
156+
# Clean temp file and restore from backup if we wrote a partial file
157+
try:
158+
os.remove(tmp)
159+
except OSError:
160+
pass
161+
if os.path.exists(kb_backup):
162+
shutil.copy2(kb_backup, kb_path)
163+
raise
164+
165+
# Apply to current console immediately (does not affect serial consoles)
166+
# Use --force so it rebuilds/loads even if it thinks nothing changed.
167+
return subprocess.run(["setupcon", "--force"], shell=False, check=True)
110168

111169

112170
def get_valid_keyboard_layouts() -> List[str]:

0 commit comments

Comments
 (0)