diff --git a/DEBIAN/control b/DEBIAN/control index b3d7026..92f91f8 100644 --- a/DEBIAN/control +++ b/DEBIAN/control @@ -1,10 +1,10 @@ Package: system-installer -Version: 2.4.3 +Version: 2.5.0 Maintainer: Thomas Castleman Homepage: https://github.com/drauger-os-development/system-installer Section: admin Architecture: amd64 Priority: important -Depends: arch-install-scripts, python3 (>=3.6.7-1~18.04), bash, gir1.2-gtk-3.0 (>=3.24.12-1ubuntu1), coreutils (>=8.28-1ubuntu1), apt (>=1.6.11), squashfs-tools (>=1:4.3-6ubuntu0.18.04.1), zenity (>=3.28.1-1), grub2-common (>=2.02-2ubuntu8.13), initramfs-tools (>=0.130ubuntu3.8), systemd (>=237-3ubuntu10.24), locales (>=2.27-3ubuntu1), procps (>=2:3.3.12-3ubuntu1.1), grep (>=3.1-2), keyboard-configuration, util-linux (>=2.34-0.1ubuntu2), python3-parted (>=3.11.2), python3-psutil (>=5.5.0), python3-apt (>=2.0.0), python3-pycurl, python3-gnupg (>=0.4.5), python3-xmltodict (>=0.11.0) +Depends: arch-install-scripts, python3 (>=3.6.7-1~18.04), bash, gir1.2-gtk-3.0 (>=3.24.12-1ubuntu1), coreutils (>=8.28-1ubuntu1), apt (>=1.6.11), squashfs-tools (>=1:4.3-6ubuntu0.18.04.1), zenity (>=3.28.1-1), grub2-common (>=2.02-2ubuntu8.13), initramfs-tools (>=0.130ubuntu3.8), systemd (>=237-3ubuntu10.24), locales (>=2.27-3ubuntu1), procps (>=2:3.3.12-3ubuntu1.1), grep (>=3.1-2), keyboard-configuration, util-linux (>=2.34-0.1ubuntu2), python3-parted (>=3.11.2), python3-psutil (>=5.5.0), python3-apt (>=2.0.0), python3-urllib3 (>=1.26.5-1~exp1), python3-gnupg (>=0.4.5), python3-xmltodict (>=0.11.0) Description: System Installer for Drauger OS System Installer for Drauger OS diff --git a/DEBIAN/postinst b/DEBIAN/postinst new file mode 100755 index 0000000..027f896 --- /dev/null +++ b/DEBIAN/postinst @@ -0,0 +1,26 @@ +#!/bin/bash +# -*- coding: utf-8 -*- +# +# postinst.sh +# +# Copyright 2023 Thomas Castleman +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# +if $(groups live | grep -vq "syslog"); then + usermod -aG syslog live +fi diff --git a/DEBIAN/postrm b/DEBIAN/postrm new file mode 100755 index 0000000..77d6812 --- /dev/null +++ b/DEBIAN/postrm @@ -0,0 +1,28 @@ +#!/bin/bash +# -*- coding: utf-8 -*- +# +# postrm.sh +# +# Copyright 2023 Thomas Castleman +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# +if [ "$1" = "purge" ]; then + user=$(grep "1000" /etc/passwd | sed 's/:/ /g' | awk '{print $1}') + groups=$(groups $user | sed "s/$user : //" | sed 's/syslog//' | sed 's/ /,/g' | sed 's/,,/,/g') + usermod -G "$groups" "$user" +fi diff --git a/etc/system-installer/settings.json b/etc/system-installer/settings.json index 40bf2b2..6d05996 100644 --- a/etc/system-installer/settings.json +++ b/etc/system-installer/settings.json @@ -3,7 +3,7 @@ "local_repo": "/run/live/medium/repo", "distro": "Drauger OS", "report": { - "recv_keys": "https://download-optimizer.draugeros.org/keys/public_keys.asc", + "recv_keys": "https://download.draugeros.org/keys/public_keys.asc", "upload": "rsync://rsync.draugeros.org/reports-upload" }, "ping servers": [ diff --git a/usr/bin/system-installer.cxx b/usr/bin/system-installer.cxx index f2bd0cb..2fe84f9 100644 --- a/usr/bin/system-installer.cxx +++ b/usr/bin/system-installer.cxx @@ -46,7 +46,7 @@ using namespace std; -str VERSION = "2.4.3"; +str VERSION = "2.5.0"; str R = "\033[0;31m"; str G = "\033[0;32m"; str Y = "\033[1;33m"; diff --git a/usr/share/system-installer/UI/confirm.py b/usr/share/system-installer/UI/confirm.py index 618f254..a7d27a7 100755 --- a/usr/share/system-installer/UI/confirm.py +++ b/usr/share/system-installer/UI/confirm.py @@ -3,7 +3,7 @@ # # confirm.py # -# Copyright 2022 Thomas Castleman +# Copyright 2023 Thomas Castleman # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ """Confirm UI for System Installer""" from __future__ import print_function from sys import argv, stderr +import auto_partitioner as ap import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk @@ -153,6 +154,19 @@ def __init__(self, settings): label12 = self._set_default_margins(label12) self.grid.attach(label12, 5, 5, 1, 1) + if ap.is_EFI(): + label31 = Gtk.Label() + label31.set_markup(" Compatibility Mode: ") + label31.set_justify(Gtk.Justification.CENTER) + label31 = self._set_default_margins(label31) + self.grid.attach(label31, 4, 6, 1, 1) + + label32 = Gtk.Label() + label32.set_markup(str(settings["COMPAT_MODE"])) + label32.set_justify(Gtk.Justification.CENTER) + label32 = self._set_default_margins(label32) + self.grid.attach(label32, 5, 6, 1, 1) + sep1 = Gtk.Separator.new(Gtk.Orientation.VERTICAL) self.grid.attach(sep1, 6, 2, 1, 6) diff --git a/usr/share/system-installer/UI/main.py b/usr/share/system-installer/UI/main.py index 46d386c..a6552f5 100755 --- a/usr/share/system-installer/UI/main.py +++ b/usr/share/system-installer/UI/main.py @@ -3,7 +3,7 @@ # # main.py # -# Copyright 2022 Thomas Castleman +# Copyright 2023 Thomas Castleman # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -92,7 +92,8 @@ def __init__(self): "disks": {"1": None, "2": None, "3": None, - "4": None}}} + "4": None}}, + "COMPAT_MODE": ""} self.langs = {'Afar': "aa", 'Afrikaans': "af", 'Aragonese': "an", 'Arabic': "ar", 'Asturian': "ast", 'Belarusian': "be", @@ -1624,7 +1625,7 @@ def options(self, button): label1 = Gtk.Label() label1.set_markup(""" - Install third-party packages such as NVIDIA drivers if necessary\t\t + Install third-party packages, such as NVIDIA drivers, if necessary\t\t """) label1.set_justify(Gtk.Justification.LEFT) label1 = self._set_default_margins(label1) @@ -1662,32 +1663,41 @@ def options(self, button): self.login = self._set_default_margins(self.login) self.grid.attach(self.login, 1, 7, 2, 1) + self.compat_mode = Gtk.CheckButton.new_with_label("Enable Bootloader Compatibility Mode") + if self.data["COMPAT_MODE"] == 1: + self.compat_mode.set_active(True) + self.compat_mode = self._set_default_margins(self.compat_mode) + + if ap.is_EFI(): + label2 = Gtk.Label() + label2.set_markup(""" + Enable compatibility mode to improve installation reliability + with some UEFI systems. Does NOT require internet.""") + label2.set_justify(Gtk.Justification.LEFT) + label2 = self._set_default_margins(label2) + self.grid.attach(label2, 1, 8, 2, 1) + + self.grid.attach(self.compat_mode, 1, 9, 2, 1) + button1 = Gtk.Button.new_with_label("Okay -->") button1.connect("clicked", self.options_next) button1 = self._set_default_margins(button1) - self.grid.attach(button1, 2, 8, 1, 1) + self.grid.attach(button1, 2, 10, 1, 1) button3 = Gtk.Button.new_with_label("<-- Back") button3.connect("clicked", self.main_menu) buton3 = self._set_default_margins(button3) - self.grid.attach(button3, 1, 8, 1, 1) + self.grid.attach(button3, 1, 10, 1, 1) self.show_all() def options_next(self, button): """Set update and extras settings""" - if self.extras.get_active(): - self.data["EXTRAS"] = 1 - else: - self.data["EXTRAS"] = 0 - # if self.updates.get_active(): - # self.data["UPDATES"] = 1 - # else: - self.data["UPDATES"] = 0 - if self.login.get_active(): - self.data["LOGIN"] = 1 - else: - self.data["LOGIN"] = 0 + self.data["EXTRAS"] = self.extras.get_active() + # self.data["UPDATES"] = self.updates.get_active() + self.data["UPDATES"] = False + self.data["LOGIN"] = self.login.get_active() + self.data["COMPAT_MODE"] = self.compat_mode.get_active() global OPTIONS_COMPLETION OPTIONS_COMPLETION = "COMPLETED" self.main_menu("clicked") diff --git a/usr/share/system-installer/UI/report.py b/usr/share/system-installer/UI/report.py index 77468db..6e48c7f 100755 --- a/usr/share/system-installer/UI/report.py +++ b/usr/share/system-installer/UI/report.py @@ -30,7 +30,7 @@ import json import gnupg import gi -import curl +import urllib3 # Configuration required to use some of these libs gi.require_version('Gtk', '3.0') @@ -313,18 +313,18 @@ def send_report(self, widget): self.show_all() try: - copyfile(self.path, "/mnt/var/mail/installation_report.txt") + copyfile(self.path, "/mnt/var/log/installation_report.txt") except: pass try: # Get keys - cURL = curl.Curl() - with open("../../../etc/system-installer/settings.json", + http = urllib3.PoolManager() + with open("/etc/system-installer/settings.json", "r") as config: URL = json.load(config)["report"] - cURL.set_url(URL["recv_keys"]) - key = cURL.get().decode() + data = http.request("GET", URL["recv_keys"]).data + key = data.decode() # Import keys result = gpg.import_keys(key) # Encrypt file using newly imported keys @@ -419,10 +419,12 @@ def generate_message(self): """write installation report to disk""" report_code = time.time() output = {} - self.path = "/var/mail/installation_report-%s.dosir" % (report_code) + self.path = "/var/log/installation_report-%s.dosir" % (report_code) output['Installation Report Code'] = report_code try: - output['system-installer Version'] = check_output(["system-installer", "-v"]).decode() + ver = check_output(["system-installer", "-v"]).decode().split("\n") + ver = [each for each in ver if each != ""][0] + output['system-installer Version'] = ver except (FileNotFoundError, CalledProcessError): output['system-installer Version'] = "VERSION UNKNOWN. LIKELY TESTING OR MAJOR ERROR." output['OS'] = get_info(["lsb_release", "-ds"])[0] @@ -445,7 +447,7 @@ def generate_message(self): if self.log.get_active(): try: with open("/tmp/system-installer.log", "r") as log: - output['INSTALLATION LOG'] = log.read() + output['INSTALLATION LOG'] = log.read().split("\n") except FileNotFoundError: output['INSTALLATION LOG'] = 'Log does not exist.' else: @@ -467,7 +469,7 @@ def generate_message(self): with open(self.path, "w+") as message: json.dump(output, message, indent=1) except PermissionError: - with open(home + "/installation_report.txt", "w+") as message: + with open(getenv("HOME") + "/installation_report.txt", "w+") as message: json.dump(output, message, indent=1) def message_accept(self, widget): @@ -597,54 +599,72 @@ def cpu_info(): # We need to create a more intelligent parser for this data as positions can # change depending on the system that is being used. sentenal = 0 - output = [] + output = {} backup_speed = None count = 0 while sentenal < 7: for each in info: if sentenal == 0: if "Model name:" in each: - output.append(each) + add = [each1 for each1 in each.split(" ") if each1 != ""] + if add[0][-1] == ":": + add[0] = add[0][:-1] + output[add[0]] = add[1] sentenal += 1 elif sentenal == 1: if "Thread(s) per core:" in each: - output.append(each) + add = [each1 for each1 in each.split(" ") if each1 != ""] + if add[0][-1] == ":": + add[0] = add[0][:-1] + output[add[0]] = int(add[1]) sentenal += 1 elif sentenal == 2: if "Core(s) per socket:" in each: - output.append(each) + add = [each1 for each1 in each.split(" ") if each1 != ""] + if add[0][-1] == ":": + add[0] = add[0][:-1] + output[add[0]] = int(add[1]) sentenal += 1 elif sentenal == 3: if "CPU max MHz:" in each: - output.append(each) + add = [each1 for each1 in each.split(" ") if each1 != ""] + if add[0][-1] == ":": + add[0] = add[0][:-1] + output[add[0]] = float(add[1]) sentenal += 1 count = 0 elif count == len(info): count = 0 sentenal += 1 - output.append("CPU max MHz:\t\t\tUnknown") + output["CPU max MHz"] = "Unknown" else: count += 1 elif sentenal == 4: if "L2 cache:" in each: - output.append(each) + add = [each1 for each1 in each.split(" ") if each1 != ""] + if add[0][-1] == ":": + add[0] = add[0][:-1] + output[add[0]] = add[1] sentenal += 1 count = 0 elif count == len(info): count = 0 sentenal += 1 - output.append("L2 cache:\t\t\tUnknown") + output["L2 cache"] = "Unknown" else: count += 1 elif sentenal == 5: if "L3 cache:" in each: - output.append(each) + add = [each1 for each1 in each.split(" ") if each1 != ""] + if add[0][-1] == ":": + add[0] = add[0][:-1] + output[add[0]] = add[1] sentenal += 1 count = 0 elif count == len(info): count = 0 sentenal += 1 - output.append("L3 cache:\t\t\tUnknown") + output["L3 cache"] = "Unknown" else: count += 1 elif sentenal == 6: @@ -668,14 +688,19 @@ def cpu_info(): speed = int(file.read()) / 1000 else: speed = backup_speed - speed = f"CPU base MHz { speed }" - output.insert(3, speed) - return "\n".join(output) + # speed = float(speed) + output["CPU base MHz"] = speed + return output def ram_info(): """Get RAM info""" ram_capacity = check_output(["lsmem", "--summary=only"]).decode().split("\n") + for each in enumerate(ram_capacity): + ram_capacity[each[0]] = [each1 for each1 in each[1].split(" ") if each1 != ""] + for each in range(len(ram_capacity) - 1, -1, -1): + if ram_capacity[each] == []: + del ram_capacity[each] swap_capacity = check_output(["swapon", "--show"]).decode().split("\n") return {"RAM": ram_capacity, "SWAP": swap_capacity} @@ -700,9 +725,3 @@ def get_info(cmd): info = info.split("\n") return info - -def send_to(): - try: - return json.loads("/etc/system-installer/settings.json")["report_to"] - except (FileNotFoundError, PermissionError, KeyError): - return "installation-reports@draugeros.org" diff --git a/usr/share/system-installer/auto_partitioner.py b/usr/share/system-installer/auto_partitioner.py index e48fee7..1459c85 100755 --- a/usr/share/system-installer/auto_partitioner.py +++ b/usr/share/system-installer/auto_partitioner.py @@ -174,7 +174,11 @@ def size_of_part(part_path, bytes=False): def get_drive_path(part_path): """Get drive path from partition path""" if ("nvme" in part_path) or ("mmc" in part_path): - output = part_path[:part_path.index("p")] + try: + output = part_path[:part_path.index("p")] + except ValueError: + # this might be a device with no partitions. + return part_path else: count = 0 for each in part_path: diff --git a/usr/share/system-installer/engine.py b/usr/share/system-installer/engine.py index 1a6fdb2..a0a1fc0 100755 --- a/usr/share/system-installer/engine.py +++ b/usr/share/system-installer/engine.py @@ -3,7 +3,7 @@ # # engine.py # -# Copyright 2022 Thomas Castleman +# Copyright 2023 Thomas Castleman # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -112,6 +112,7 @@ def copy_log_to_disk(): SETTINGS = UI.main.show_main(boot_time=BOOT_TIME) try: if ((SETTINGS == 1) or (len(SETTINGS) == 0)): + print("Returned data from UI appears empty. Assuming installation was canceled...") sys.exit(1) elif SETTINGS == 2: UI.error.show_error(""" diff --git a/usr/share/system-installer/installer.py b/usr/share/system-installer/installer.py index 03fa230..a1f2e2d 100755 --- a/usr/share/system-installer/installer.py +++ b/usr/share/system-installer/installer.py @@ -3,7 +3,7 @@ # # installer.py # -# Copyright 2022 Thomas Castleman +# Copyright 2023 Thomas Castleman # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/usr/share/system-installer/modules/master.py b/usr/share/system-installer/modules/master.py index 2562786..8ee3ca1 100755 --- a/usr/share/system-installer/modules/master.py +++ b/usr/share/system-installer/modules/master.py @@ -3,7 +3,7 @@ # # master.py # -# Copyright 2022 Thomas Castleman +# Copyright 2023 Thomas Castleman # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ """ from __future__ import print_function from sys import argv, stderr -import subprocess +import subprocess as subproc import multiprocessing import os from shutil import rmtree, copyfile @@ -145,16 +145,16 @@ def set_passwd(USERNAME, PASSWORD): if PASSWORD == "OEM": PASSWORD = "toor" if USERNAME != "drauger-user": - process = subprocess.Popen("chpasswd", + process = subproc.Popen("chpasswd", stdout=stderr.buffer, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE) + stdin=subproc.PIPE, + stderr=subproc.PIPE) process.communicate(input=bytes(r"root:%s" % (PASSWORD), "utf-8")) - process = subprocess.Popen("chpasswd", + process = subproc.Popen("chpasswd", stdout=stderr.buffer, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE) + stdin=subproc.PIPE, + stderr=subproc.PIPE) process.communicate(input=bytes(r"%s:%s" % (USERNAME, PASSWORD), "utf-8")) @@ -201,7 +201,7 @@ def set_keyboard(MODEL, LAYOUT, VARIENT): """ with open("/etc/default/keyboard", "w+") as xkb_default: xkb_default.write(xkb_file) - subprocess.Popen(["udevadm", "trigger", "--subsystem-match=input", + subproc.Popen(["udevadm", "trigger", "--subsystem-match=input", "--action=change"], stdout=stderr.buffer) def remove_launcher(USERNAME): @@ -221,7 +221,7 @@ def remove_launcher(USERNAME): def set_plymouth_theme(): """Ensure the plymouth theme is set correctly""" - subprocess.Popen(["update-alternatives", "--install", + subproc.Popen(["update-alternatives", "--install", "/usr/share/plymouth/themes/default.plymouth", "default.plymouth", "/usr/share/plymouth/themes/drauger-theme/drauger-theme.plymouth", @@ -230,11 +230,11 @@ def set_plymouth_theme(): "default.plymouth.grub", "/usr/share/plymouth/themes/drauger-theme/drauger-theme.grub"], stdout=stderr.buffer) - process = subprocess.Popen(["update-alternatives", "--config", + process = subproc.Popen(["update-alternatives", "--config", "default.plymouth"], stdout=stderr.buffer, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE) + stdin=subproc.PIPE, + stderr=subproc.PIPE) process.communicate(input=bytes("2\n", "utf-8")) @@ -244,22 +244,22 @@ def install_kernel(release): # it's just easier and more reliable packages = ["linux-headers-" + release, "linux-image-" + release] install_command = ["dpkg", "--install"] - subprocess.check_call(["apt-get", "purge", "-y"] + packages, + subproc.check_call(["apt-get", "purge", "-y"] + packages, stdout=stderr.buffer) - subprocess.check_call(["apt-get", "autoremove", "-y", "--purge"], + subproc.check_call(["apt-get", "autoremove", "-y", "--purge"], stdout=stderr.buffer) packages = [each for each in os.listdir("/repo") if "linux-" in each] os.chdir("/repo") - subprocess.check_call(install_command + packages, stdout=stderr.buffer) + subproc.check_call(install_command + packages, stdout=stderr.buffer) os.chdir("/") -def install_bootloader(efi, root, release, distro): +def install_bootloader(efi, root, release, distro, compat_mode): """Determine whether bootloader needs to be systemd-boot (for UEFI) or GRUB (for BIOS) and install the correct one.""" if efi not in ("NULL", None, "", False): - _install_systemd_boot(release, root, distro) + _install_systemd_boot(release, root, distro, compat_mode) else: _install_grub(root) @@ -280,25 +280,25 @@ def _install_grub(root): redo = False os.mkdir("/boot/grub") try: - subprocess.check_call(["grub-mkdevicemap", "--verbose"], + subproc.check_call(["grub-mkdevicemap", "--verbose"], stdout=stderr.buffer) - except subprocess.CalledProcessError: + except subproc.CalledProcessError: redo = True - subprocess.check_call(["grub-install", "--verbose", "--force", + subproc.check_call(["grub-install", "--verbose", "--force", "--target=i386-pc", root], stdout=stderr.buffer) if redo: try: - subprocess.check_call(["grub-mkdevicemap", "--verbose"], + subproc.check_call(["grub-mkdevicemap", "--verbose"], stdout=stderr.buffer) - except subprocess.CalledProcessError as e: + except subproc.CalledProcessError as e: eprint("WARNING: GRUB device map failed to generate") eprint("The error was as follows:") eprint(traceback.format_exeception()) - subprocess.check_call(["grub-mkconfig", "-o", "/boot/grub/grub.cfg"], + subproc.check_call(["grub-mkconfig", "-o", "/boot/grub/grub.cfg"], stdout=stderr.buffer) -def _install_systemd_boot(release, root, distro): +def _install_systemd_boot(release, root, distro, compat_mode): """set up and install systemd-boot""" try: os.makedirs("/boot/efi/loader/entries", exist_ok=True) @@ -312,9 +312,9 @@ def _install_systemd_boot(release, root, distro): with open("/etc/environment", "a") as envi: envi.write("export SYSTEMD_RELAX_ESP_CHECKS=1") try: - subprocess.check_call(["bootctl", "--path=/boot/efi", "install"], + subproc.check_call(["bootctl", "--path=/boot/efi", "install"], stdout=stderr.buffer) - except subprocess.CalledProcessError as e: + except subproc.CalledProcessError as e: eprint("WARNING: bootctl issued CalledProcessError:") eprint(e) eprint("Performing manual installation of systemd-boot.") @@ -340,19 +340,19 @@ def _install_systemd_boot(release, root, distro): "/boot/efi/EFI/systemd/systemd-bootx64.efi") except FileExistsError: pass - with open("/boot/efi/loader/loader.conf", "w+") as loader_conf: - loader_conf.write(f"default {distro}\n") - loader_conf.write("timeout 5\nconsole-mode 1\neditor 1") - try: - subprocess.check_call(["chattr", "-i", "/boot/efi/loader/loader.conf"], - stdout=stderr.buffer) - except subprocess.CalledProcessError: - eprint("CHATTR FAILED ON loader.conf, setting octal permissions to 444") - os.chmod("/boot/efi/loader/loader.conf", 0o444) + # with open("/boot/efi/loader/loader.conf", "w+") as loader_conf: + # loader_conf.write(f"default {distro}\n") + # loader_conf.write("timeout 5\nconsole-mode auto\neditor 1") + # try: + # subproc.check_call(["chattr", "-i", "/boot/efi/loader/loader.conf"], + # stdout=stderr.buffer) + # except subproc.CalledProcessError: + # eprint("CHATTR FAILED ON loader.conf, setting octal permissions to 444") + # os.chmod("/boot/efi/loader/loader.conf", 0o444) install_command = ["dpkg", "--install"] packages = [each for each in os.listdir("/repo") if "systemd-boot-manager" in each] os.chdir("/repo") - depends = subprocess.check_output(["dpkg", "-f"] + packages + ["depends"]) + depends = subproc.check_output(["dpkg", "-f"] + packages + ["depends"]) depends = depends.decode()[:-1].split(", ") # List of dependencies depends = [depends[each[0]].split(" ")[0] for each in enumerate(depends)] @@ -364,35 +364,40 @@ def _install_systemd_boot(release, root, distro): if ((each1 in each) and (each not in packages)): packages.append(each) break - subprocess.check_call(install_command + packages, + subproc.check_call(install_command + packages, stdout=stderr.buffer) os.chdir("/") - subprocess.check_call(["systemd-boot-manager", "-e"], + if compat_mode: + subproc.check_call(["systemd-boot-manager", "--compat-mode=enable"], + stdout=stderr.buffer) + subproc.check_call(["systemd-boot-manager", "-e"], + stdout=stderr.buffer) + subproc.check_call(["systemd-boot-manager", "--apply-loader-config"], stdout=stderr.buffer) - subprocess.check_call(["systemd-boot-manager", "-r"], + subproc.check_call(["systemd-boot-manager", "-r"], stdout=stderr.buffer) - subprocess.check_call(["systemd-boot-manager", + subproc.check_call(["systemd-boot-manager", "--enforce-default-entry=enable"], stdout=stderr.buffer) # This lib didn't exist before we installed this package. # So we can only now import it import systemd_boot_manager systemd_boot_manager.update_defaults_file(distro + ".conf") - subprocess.check_call(["systemd-boot-manager", "-u"], + subproc.check_call(["systemd-boot-manager", "-u"], stdout=stderr.buffer) check_systemd_boot(release, root, distro) -def setup_lowlevel(efi, root, distro): +def setup_lowlevel(efi, root, distro, compat_mode): """Set up kernel and bootloader""" - release = subprocess.check_output(["uname", "--release"]).decode()[0:-1] + release = subproc.check_output(["uname", "--release"]).decode()[0:-1] install_kernel(release) set_plymouth_theme() __update__(91) eprint("\n ### MAKING INITRAMFS ### ") - subprocess.check_call(["mkinitramfs", "-o", "/boot/initrd.img-" + release], + subproc.check_call(["mkinitramfs", "-o", "/boot/initrd.img-" + release], stdout=stderr.buffer) - install_bootloader(efi, root, release, distro) + install_bootloader(efi, root, release, distro, compat_mode) sleep(0.5) os.symlink("/boot/initrd.img-" + release, "/boot/initrd.img") os.symlink("/boot/vmlinuz-" + release, "/boot/vmlinuz") @@ -404,7 +409,7 @@ def check_systemd_boot(release, root, distro): root_flags = "quiet splash" recovery_flags = "ro recovery nomodeset" # Get Root UUID - uuid = subprocess.check_output(["blkid", "-s", "PARTUUID", + uuid = subproc.check_output(["blkid", "-s", "PARTUUID", "-o", "value", root]).decode()[0:-1] # Check for standard boot config @@ -500,8 +505,8 @@ def _check_for_laptop(): Returns True if it is a laptop, returns False otherwise. """ try: - subprocess.check_call(["laptop-detect"]) - except subprocess.CalledProcessError: + subproc.check_call(["laptop-detect"]) + except subproc.CalledProcessError: return False return True @@ -522,7 +527,8 @@ def install(settings, distro): del processes_to_do[each] MainInstallation(processes_to_do, settings) handle_laptops(settings["USERNAME"]) - setup_lowlevel(settings["EFI"], settings["ROOT"], distro) + setup_lowlevel(settings["EFI"], settings["ROOT"], distro, + settings["COMPAT_MODE"]) verify(settings["USERNAME"], settings["ROOT"], distro) if "PURGE" in settings: purge_package(settings["PURGE"]) diff --git a/usr/share/system-installer/tests/test_auto_partitioner.py b/usr/share/system-installer/tests/test_auto_partitioner.py index 81c0d67..a2284bf 100755 --- a/usr/share/system-installer/tests/test_auto_partitioner.py +++ b/usr/share/system-installer/tests/test_auto_partitioner.py @@ -24,6 +24,8 @@ """Tests for auto_partitioner""" import auto_partitioner as ap import os +import random +import json def test_get_min_root_size_with_swap_part(): """Make sure our math is correct to get the min root size, assuming no swap partition""" @@ -45,3 +47,114 @@ def test_get_min_root_size_without_swap_part(): def test_is_EFI(): """Check if the checks if a system is an EFI system are working""" assert os.path.isdir("/boot/efi") == ap.is_EFI() + + +def test_get_drive_path(): + """Ensure drive paths are always returned correctly""" + # initial setup + nvme = "/dev/nvme" + mmcblk = "/dev/mmcblk" + sata = "/dev/sd" + ide = "/dev/hd" + drive_letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", + "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", + "w", "x", "y", "z"] + nvme_list = {} + mmcblk_list = {} + sata_list = {} + ide_list = {} + # NVMe setup + for each in range(random.randint(1, 45)): + nvme_list[nvme + str(each)] = {} + for each in nvme_list: + total_drive_count = random.randint(1, 256) + for current_drive_count in range(1, total_drive_count): + nvme_list[each][each + "n" + str(current_drive_count)] = [] + """ + At this point, `nvme_list` should be set up like this: + + { + "/dev/nvme1": { + "/dev/nvme1n1": [], + "/dev/nvme1n2": [] + }, + "/dev/nvme2": { + "/dev/nvme2n1": [], + "/dev/nvme2n2": [] + } + } + + But we want this: + + { + "/dev/nvme1": { + "/dev/nvme1n1": [ + "/dev/nvme1n1p1", + "/dev/nvme1n1p2", + "/dev/nvme1n1p3" + ], + "/dev/nvme1n2": ["/dev/nvme1n2p1"] + }, + "/dev/nvme2": { + "/dev/nvme2n1": [ + "/dev/nvme2n1p1", + "/dev/nvme2n1p2" + ], + "/dev/nvme2n2": [ + "/dev/nvme2n2p1", + "/dev/nvme2n2p2", + "/dev/nvme2n2p3", + "/dev/nvme2n2p4" + ] + } + } + + Just, larger. + """ + for each in nvme_list: + for each1 in nvme_list[each]: + for part_count in range(1, random.randint(1, 256)): + nvme_list[each][each1].append(each1 + "p" + str(part_count)) + # now we need to set up mmcblk, SATA, and IDE drive examples. Should be simple... + # SATA set up + for each in range(random.randint(1, len(drive_letters) - 1)): + sata_list[sata + drive_letters[each]] = [] + for each in range(1, random.randint(1, 256)): + for each1 in sata_list: + sata_list[each1].append(each1 + str(each)) + # IDE set up + for each in range(random.randint(1, len(drive_letters) - 1)): + ide_list[ide + drive_letters[each]] = [] + for each in range(1, random.randint(1, 256)): + for each1 in ide_list: + ide_list[each1].append(each1 + str(each)) + # now just to setup for mmcblk + for each in range(random.randint(1, 45)): + mmcblk_list[mmcblk + str(each)] = [] + for each in mmcblk_list: + total_drive_count = random.randint(1, 256) + for current_drive_count in range(1, total_drive_count): + mmcblk_list[each].append(each + "p" + str(current_drive_count)) + # check to make sure everything is valid + # check mmcblk + for each in mmcblk_list: + for each1 in mmcblk_list[each]: + assert each == ap.get_drive_path(each1) + assert each == ap.get_drive_path(each) + # check NVMe + for each in nvme_list: + # this, outter-most, loop needs no checks + for each1 in nvme_list[each]: + for each2 in nvme_list[each][each1]: + assert each1 == ap.get_drive_path(each2) + assert each1 == ap.get_drive_path(each1) + # check SATA + for each in sata_list: + for each1 in sata_list[each]: + assert each == ap.get_drive_path(each1) + assert each == ap.get_drive_path(each) + # check IDE + for each in ide_list: + for each1 in ide_list[each]: + assert each == ap.get_drive_path(each1) + assert each == ap.get_drive_path(each)