Make proper text-based initial setup UI
Re-use the same kickstart code to configure the system in text mode too.
This commit is contained in:
parent
556b584f74
commit
32b3a210e0
@ -1,160 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Failsafe minimal text-mode firstboot
|
|
||||||
|
|
||||||
# Welcome
|
|
||||||
|
|
||||||
if [ "x$1" = "x--help" ]; then
|
|
||||||
echo "Failsafe minimal text-mode firstboot"
|
|
||||||
echo "For unattended mode use: $0 <username> <userpass> <vm-creation-option-number>"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "########################################################"
|
|
||||||
echo " Welcome to `cat /etc/qubes-release`"
|
|
||||||
echo "########################################################"
|
|
||||||
echo
|
|
||||||
echo "This is failsafe text-mode firstboot. If you see this message, you have"
|
|
||||||
echo "some problem with Xorg (most probably video driver)"
|
|
||||||
echo
|
|
||||||
echo "Anyway, some basic setup is needed to continue:"
|
|
||||||
|
|
||||||
# User creation
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "1. Setup user account"
|
|
||||||
exists=0
|
|
||||||
user=$1
|
|
||||||
while [ -z "$user" ]; do
|
|
||||||
echo -n "Enter desired username (may already exist): "
|
|
||||||
read user
|
|
||||||
if echo "$user" | grep -q "[^a-z0-9]"; then
|
|
||||||
echo "ERROR: Invalid characters in username, try again"
|
|
||||||
user=
|
|
||||||
elif id $user > /dev/null 2>&1; then
|
|
||||||
if [ $(id -u ${user}) -ge 1000 ] && id -n -G ${user} | grep -q qubes; then
|
|
||||||
echo "OK: Using an existing user: \"${user}\""
|
|
||||||
exists=1
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "ERROR: This user already exists or is not suitable. Please try again"
|
|
||||||
user=
|
|
||||||
else
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ ${exists} -eq 0 ]; then
|
|
||||||
useradd -G qubes -m "$user" || exit 1
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
echo -e "$2\n$2" | passwd --stdin "$user"
|
|
||||||
else
|
|
||||||
while ! passwd "$user"; do true; done
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create default VMs
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "2. Create default VMs"
|
|
||||||
echo
|
|
||||||
echo "Choose one option:"
|
|
||||||
echo " 1. Create default service VMs, and pre-defined AppVMs (personal, work, untrusted, vault)"
|
|
||||||
echo " 2. Just create default service VMs"
|
|
||||||
echo " 3. Do not create any VMs right now, but configure template(s)"
|
|
||||||
echo " 4. Do not do anything (not recommended, for advanced users only)"
|
|
||||||
vms_option=$3
|
|
||||||
while true; do
|
|
||||||
if [ -z "$vms_option" ]; then
|
|
||||||
echo -n "Enter your choice (1/2/3/4): "
|
|
||||||
read vms_option
|
|
||||||
fi
|
|
||||||
if [ "$vms_option" == "1" ]; then
|
|
||||||
vms_template=yes
|
|
||||||
vms_service=yes
|
|
||||||
vms_app=yes
|
|
||||||
break
|
|
||||||
elif [ "$vms_option" == "2" ]; then
|
|
||||||
vms_template=yes
|
|
||||||
vms_service=yes
|
|
||||||
break
|
|
||||||
elif [ "$vms_option" == "3" ]; then
|
|
||||||
vms_template=yes
|
|
||||||
break
|
|
||||||
elif [ "$vms_option" == "4" ]; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
echo "ERROR: Invalid choice, try again"
|
|
||||||
vms_option=
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
for service in rdisc kdump libvirt-guests salt-minion; do
|
|
||||||
systemctl disable ${service}.service || :
|
|
||||||
systemctl stop ${service}.service || :
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$vms_template" == "yes" ]; then
|
|
||||||
for template in `ls /var/lib/qubes/vm-templates`; do
|
|
||||||
echo "-> Configuring template $template..."
|
|
||||||
qvm-start --no-guid $template
|
|
||||||
su -g "qubes" -c "qvm-sync-appmenus $template" - $user
|
|
||||||
qvm-shutdown --wait $template
|
|
||||||
done
|
|
||||||
|
|
||||||
qubes-prefs --set default-template 'fedora-29'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$vms_service" == "yes" -o "$vms_app" == "yes" ]; then
|
|
||||||
echo "-> Configuring Qubes OS management framework..."
|
|
||||||
|
|
||||||
if test -e /var/log/salt/minion; then
|
|
||||||
mv /var/log/salt/minion /var/log/salt/minion.install || :
|
|
||||||
fi
|
|
||||||
|
|
||||||
qubesctl saltutil.clear_cache -l quiet --out quiet
|
|
||||||
qubesctl saltutil.sync_all -l quiet --out quiet
|
|
||||||
fi
|
|
||||||
|
|
||||||
states=()
|
|
||||||
|
|
||||||
if [ "$vms_service" == "yes" ]; then
|
|
||||||
states=("${states[@]}" qvm.sys-net qvm.sys-firewall)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$vms_app" == "yes" ]; then
|
|
||||||
states=("${states[@]}" qvm.personal qvm.work qvm.untrusted qvm.vault)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$vms_service" == "yes" -o "$vms_app" == "yes" ]; then
|
|
||||||
for state in "${states[@]}"; do
|
|
||||||
echo "-> Requesting creation of VM: ${state#qvm.}"
|
|
||||||
qubesctl top.enable "${state}" -l quiet --out quiet
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "-> Creating VMs"
|
|
||||||
qubesctl "state.highstate"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$vms_service" == "yes" ]; then
|
|
||||||
echo "--> Configuring service VMs"
|
|
||||||
default_netvm="sys-net"
|
|
||||||
default_firewallvm="sys-firewall"
|
|
||||||
|
|
||||||
su -g "qubes" -c "qvm-prefs --set ${default_firewallvm} netvm ${default_netvm}" - $user
|
|
||||||
su -g "qubes" -c "qubes-prefs --set default-netvm ${default_firewallvm}" - $user
|
|
||||||
su -g "qubes" -c "qubes-prefs --set updatevm ${default_firewallvm}" - $user
|
|
||||||
su -g "qubes" -c "qubes-prefs --set clockvm ${default_netvm}" - $user
|
|
||||||
|
|
||||||
echo "-> Starting network..."
|
|
||||||
service qubes-netvm start
|
|
||||||
|
|
||||||
# DispVM creation fails with the following error message, most likely due to missing $DISPLAY:
|
|
||||||
# "Cannot start qubes-guid!"
|
|
||||||
#echo "-> Creating DispVM savefile (can take long time)..."
|
|
||||||
#su -g "qubes" -c "/usr/bin/qvm-create-default-dvm --default-template --default-script" - $user || :
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "-> Done."
|
|
@ -178,6 +178,8 @@ class QubesData(AddonData):
|
|||||||
def set_stage(self, stage):
|
def set_stage(self, stage):
|
||||||
if self.thread_dialog is not None:
|
if self.thread_dialog is not None:
|
||||||
self.thread_dialog.set_text(stage)
|
self.thread_dialog.set_text(stage)
|
||||||
|
else:
|
||||||
|
print(stage)
|
||||||
|
|
||||||
def do_setup(self):
|
def do_setup(self):
|
||||||
qubes_gid = grp.getgrnam('qubes').gr_gid
|
qubes_gid = grp.getgrnam('qubes').gr_gid
|
||||||
|
@ -27,11 +27,11 @@
|
|||||||
_ = lambda x: x
|
_ = lambda x: x
|
||||||
N_ = lambda x: x
|
N_ = lambda x: x
|
||||||
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from pyanaconda.ui.categories.system import SystemCategory
|
from pyanaconda.ui.categories.system import SystemCategory
|
||||||
from pyanaconda.ui.tui.spokes import NormalTUISpoke
|
from pyanaconda.ui.tui.spokes import NormalTUISpoke
|
||||||
from pyanaconda.constants_text import INPUT_PROCESSED, INPUT_DISCARDED
|
from simpleline.render.containers import ListColumnContainer
|
||||||
|
from simpleline.render.widgets import CheckboxWidget
|
||||||
|
from simpleline.render.screen import InputState
|
||||||
from pyanaconda.ui.common import FirstbootOnlySpokeMixIn
|
from pyanaconda.ui.common import FirstbootOnlySpokeMixIn
|
||||||
|
|
||||||
# export only the HelloWorldSpoke and HelloWorldEditSpoke classes
|
# export only the HelloWorldSpoke and HelloWorldEditSpoke classes
|
||||||
@ -51,21 +51,14 @@ class QubesOsSpoke(FirstbootOnlySpokeMixIn, NormalTUISpoke):
|
|||||||
|
|
||||||
### class attributes defined by API ###
|
### class attributes defined by API ###
|
||||||
|
|
||||||
# title of the spoke
|
|
||||||
title = N_("Qubes OS")
|
|
||||||
|
|
||||||
# category this spoke belongs to
|
# category this spoke belongs to
|
||||||
category = SystemCategory
|
category = SystemCategory
|
||||||
|
|
||||||
def __init__(self, app, data, storage, payload, instclass):
|
def __init__(self, data, storage, payload, instclass):
|
||||||
"""
|
"""
|
||||||
:see: pyanaconda.ui.tui.base.UIScreen
|
:see: pyanaconda.ui.tui.base.UIScreen
|
||||||
:see: pyanaconda.ui.tui.base.App
|
:see: pyanaconda.ui.tui.base.App
|
||||||
:param app: reference to application which is a main class for TUI
|
|
||||||
screen handling, it is responsible for mainloop control
|
|
||||||
and keeping track of the stack where all TUI screens are
|
|
||||||
scheduled
|
|
||||||
:type app: instance of pyanaconda.ui.tui.base.App
|
|
||||||
:param data: data object passed to every spoke to load/store data
|
:param data: data object passed to every spoke to load/store data
|
||||||
from/to it
|
from/to it
|
||||||
:type data: pykickstart.base.BaseHandler
|
:type data: pykickstart.base.BaseHandler
|
||||||
@ -79,9 +72,22 @@ class QubesOsSpoke(FirstbootOnlySpokeMixIn, NormalTUISpoke):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
|
NormalTUISpoke.__init__(self, data, storage, payload, instclass)
|
||||||
|
|
||||||
|
self.initialize_start()
|
||||||
|
|
||||||
|
# title of the spoke
|
||||||
|
self.title = N_("Qubes OS")
|
||||||
|
|
||||||
|
self._container = None
|
||||||
|
|
||||||
|
self.qubes_data = self.data.addons.org_qubes_os_initial_setup
|
||||||
|
|
||||||
|
for attr in self.qubes_data.bool_options:
|
||||||
|
setattr(self, '_' + attr, getattr(self.qubes_data, attr))
|
||||||
|
|
||||||
|
self.initialize_done()
|
||||||
|
|
||||||
self.done = False
|
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""
|
"""
|
||||||
@ -95,6 +101,10 @@ class QubesOsSpoke(FirstbootOnlySpokeMixIn, NormalTUISpoke):
|
|||||||
|
|
||||||
NormalTUISpoke.initialize(self)
|
NormalTUISpoke.initialize(self)
|
||||||
|
|
||||||
|
def _add_checkbox(self, name, title):
|
||||||
|
w = CheckboxWidget(title=title, completed=getattr(self, name))
|
||||||
|
self._container.add(w, self._set_checkbox, name)
|
||||||
|
|
||||||
def refresh(self, args=None):
|
def refresh(self, args=None):
|
||||||
"""
|
"""
|
||||||
The refresh method that is called every time the spoke is displayed.
|
The refresh method that is called every time the spoke is displayed.
|
||||||
@ -110,8 +120,44 @@ class QubesOsSpoke(FirstbootOnlySpokeMixIn, NormalTUISpoke):
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
super(QubesOsSpoke, self).refresh()
|
||||||
|
self._container = ListColumnContainer(1)
|
||||||
|
|
||||||
return True
|
w = CheckboxWidget(title=_('Create default system qubes '
|
||||||
|
'(sys-net, sys-firewall, default DispVM)'),
|
||||||
|
completed=self._system_vms)
|
||||||
|
self._container.add(w, self._set_checkbox, '_system_vms')
|
||||||
|
w = CheckboxWidget(title=_('Create default application qubes '
|
||||||
|
'(personal, work, untrusted, vault)'),
|
||||||
|
completed=self._default_vms)
|
||||||
|
self._container.add(w, self._set_checkbox, '_default_vms')
|
||||||
|
if self.qubes_data.whonix_available:
|
||||||
|
w = CheckboxWidget(
|
||||||
|
title=_('Create Whonix Gateway and Workstation qubes '
|
||||||
|
'(sys-whonix, anon-whonix)'),
|
||||||
|
completed=self._whonix_vms)
|
||||||
|
self._container.add(w, self._set_checkbox, '_whonix_vms')
|
||||||
|
if self._whonix_vms:
|
||||||
|
w = CheckboxWidget(
|
||||||
|
title=_('Enable system and template updates over the Tor anonymity '
|
||||||
|
'network using Whonix'),
|
||||||
|
completed=self._whonix_default)
|
||||||
|
self._container.add(w, self._set_checkbox, '_whonix_default')
|
||||||
|
if self.qubes_data.usbvm_available:
|
||||||
|
w = CheckboxWidget(
|
||||||
|
title=_('Create USB qube holding all USB controllers (sys-usb)'),
|
||||||
|
completed=self._usbvm)
|
||||||
|
self._container.add(w, self._set_checkbox, '_usbvm')
|
||||||
|
if self._usbvm:
|
||||||
|
w = CheckboxWidget(
|
||||||
|
title=_('Use sys-net qube for both networking and USB devices'),
|
||||||
|
completed=self._usbvm_with_netvm)
|
||||||
|
self._container.add(w, self._set_checkbox, '_usbvm_with_netvm')
|
||||||
|
|
||||||
|
self.window.add_with_separator(self._container)
|
||||||
|
|
||||||
|
def _set_checkbox(self, name):
|
||||||
|
setattr(self, name, not getattr(self, name))
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
"""
|
"""
|
||||||
@ -120,10 +166,10 @@ class QubesOsSpoke(FirstbootOnlySpokeMixIn, NormalTUISpoke):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc = subprocess.Popen(["/usr/share/qubes/firstboot-qubes-text"])
|
for attr in self.qubes_data.bool_options:
|
||||||
proc.wait()
|
setattr(self.qubes_data, attr, getattr(self, '_' + attr))
|
||||||
|
|
||||||
self.done = True
|
self.qubes_data.seen = True
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
"""
|
"""
|
||||||
@ -147,7 +193,7 @@ class QubesOsSpoke(FirstbootOnlySpokeMixIn, NormalTUISpoke):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.done
|
return self.qubes_data.seen
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
@ -179,28 +225,8 @@ class QubesOsSpoke(FirstbootOnlySpokeMixIn, NormalTUISpoke):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if key:
|
if self._container.process_user_input(key):
|
||||||
# Do something with the user's input.
|
self.apply()
|
||||||
pass
|
return InputState.PROCESSED_AND_REDRAW
|
||||||
|
|
||||||
# no other actions scheduled, apply changes
|
return super().input(args, key)
|
||||||
self.apply()
|
|
||||||
|
|
||||||
# close the current screen (remove it from the stack)
|
|
||||||
self.close()
|
|
||||||
return INPUT_PROCESSED
|
|
||||||
|
|
||||||
def prompt(self, args=None):
|
|
||||||
"""
|
|
||||||
The prompt method that is called by the main loop to get the prompt
|
|
||||||
for this screen.
|
|
||||||
|
|
||||||
:param args: optional argument that can be passed to App.switch_screen*
|
|
||||||
methods
|
|
||||||
:type args: anything
|
|
||||||
:return: text that should be used in the prompt for the input
|
|
||||||
:rtype: unicode|None
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
return _("Please press enter to start Qubes OS configuration.")
|
|
||||||
|
@ -25,16 +25,12 @@ at first boot time.
|
|||||||
%install
|
%install
|
||||||
rm -rf $RPM_BUILD_ROOT
|
rm -rf $RPM_BUILD_ROOT
|
||||||
|
|
||||||
install -d $RPM_BUILD_ROOT/%{_datadir}/qubes
|
|
||||||
install --mode 0755 firstboot-qubes-text $RPM_BUILD_ROOT/%{_datadir}/qubes/firstboot-qubes-text
|
|
||||||
|
|
||||||
install -d $RPM_BUILD_ROOT/%{_datadir}/anaconda/addons
|
install -d $RPM_BUILD_ROOT/%{_datadir}/anaconda/addons
|
||||||
cp -a org_qubes_os_initial_setup $RPM_BUILD_ROOT/%{_datadir}/anaconda/addons/
|
cp -a org_qubes_os_initial_setup $RPM_BUILD_ROOT/%{_datadir}/anaconda/addons/
|
||||||
|
|
||||||
%files
|
%files
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
%doc LICENSE README
|
%doc LICENSE README
|
||||||
%{_datadir}/qubes/firstboot-qubes-text
|
|
||||||
%dir %{_datadir}/anaconda/addons/org_qubes_os_initial_setup
|
%dir %{_datadir}/anaconda/addons/org_qubes_os_initial_setup
|
||||||
%{_datadir}/anaconda/addons/org_qubes_os_initial_setup/*
|
%{_datadir}/anaconda/addons/org_qubes_os_initial_setup/*
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user