From 55b7703fd65dddefd9eca7f2b48f02a209cf8983 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 16 Feb 2021 17:03:08 +0000 Subject: [PATCH] Split into GUI and Daemon (#478) --- common/DBusStructures.vala | 92 ++++ common/meson.build | 3 + daemon/Daemon.vala | 517 +++++++++++++++++++ daemon/io.elementary.InstallerDaemon.conf | 17 + daemon/io.elementary.InstallerDaemon.service | 11 + daemon/meson.build | 25 + meson.build | 19 +- src/Helpers/InstallerDaemon.vala | 128 +++++ src/Helpers/LogHelper.vala | 18 +- src/MainWindow.vala | 9 +- src/Objects/Configuration.vala | 2 +- src/Objects/Mount.vala | 27 +- src/Utils.vala | 25 +- src/Views/CheckView.vala | 30 +- src/Views/DiskView.vala | 38 +- src/Views/PartitioningView.vala | 138 ++--- src/Views/ProgressView.vala | 364 ++----------- src/Widgets/DecryptMenu.vala | 66 ++- src/Widgets/PartitionBar.vala | 24 +- src/Widgets/PartitionMenu.vala | 10 +- src/meson.build | 21 +- 21 files changed, 1052 insertions(+), 532 deletions(-) create mode 100644 common/DBusStructures.vala create mode 100644 common/meson.build create mode 100644 daemon/Daemon.vala create mode 100644 daemon/io.elementary.InstallerDaemon.conf create mode 100644 daemon/io.elementary.InstallerDaemon.service create mode 100644 daemon/meson.build create mode 100644 src/Helpers/InstallerDaemon.vala diff --git a/common/DBusStructures.vala b/common/DBusStructures.vala new file mode 100644 index 000000000..388e2b9ed --- /dev/null +++ b/common/DBusStructures.vala @@ -0,0 +1,92 @@ +/* + * Copyright 2021 elementary, Inc. + * + * 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 3 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, see . + */ + +public struct InstallerDaemon.DiskInfo { + Disk[] physical_disks; + Disk[] logical_disks; +} + +public struct InstallerDaemon.Disk { + string name; + string device_path; + uint64 sectors; + uint64 sector_size; + bool rotational; + bool removable; + + Partition[] partitions; +} + +public struct InstallerDaemon.Partition { + string device_path; + + Distinst.FileSystem filesystem; + + uint64 start_sector; + uint64 end_sector; + Distinst.PartitionUsage sectors_used; + string? current_lvm_volume_group; +} + +public struct InstallerDaemon.InstallConfig { + string hostname; + string keyboard_layout; + string keyboard_variant; + string lang; + uint8 flags; +} + +[Flags] +public enum InstallerDaemon.MountFlags { + FORMAT = 1, + LVM = 2, + LVM_ON_LUKS = 4 +} + +public struct InstallerDaemon.Mount { + string partition_path; + string parent_disk; + string mount_point; + uint64 sectors; + Distinst.FileSystem filesystem; + MountFlags flags; + + public bool is_valid_boot_mount () { + return filesystem == Distinst.FileSystem.FAT16 + || filesystem == Distinst.FileSystem.FAT32; + } + + public bool is_valid_root_mount () { + return filesystem != Distinst.FileSystem.FAT16 + && filesystem != Distinst.FileSystem.FAT32 + && filesystem != Distinst.FileSystem.NTFS; + } + + public bool is_lvm () { + return MountFlags.LVM in flags; + } + + public bool should_format () { + return MountFlags.FORMAT in flags; + } +} + +public struct InstallerDaemon.LuksCredentials { + string device; + string pv; + string password; +} diff --git a/common/meson.build b/common/meson.build new file mode 100644 index 000000000..47423d01f --- /dev/null +++ b/common/meson.build @@ -0,0 +1,3 @@ +common_files = files( + 'DBusStructures.vala', +) diff --git a/daemon/Daemon.vala b/daemon/Daemon.vala new file mode 100644 index 000000000..9cb9b51ad --- /dev/null +++ b/daemon/Daemon.vala @@ -0,0 +1,517 @@ +/* + * Copyright 2021 elementary, Inc. + * + * 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 3 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, see . + */ + +private static GLib.MainLoop loop; + +[DBus (name = "io.elementary.InstallerDaemon")] +public class InstallerDaemon.Daemon : GLib.Object { + public signal void on_log_message (Distinst.LogLevel level, string message); + public signal void on_status (Distinst.Status status); + public signal void on_error (Distinst.Error error); + + private Distinst.Disks disks; + + construct { + Distinst.log ((level, message) => { + Idle.add (() => { + on_log_message (level, message); + }); + }); + } + + public Distinst.PartitionTable bootloader_detect () throws GLib.Error { + return Distinst.bootloader_detect (); + } + + public DiskInfo get_disks (bool get_partitions = false) throws GLib.Error { + disks = Distinst.Disks.probe (); + + if (get_partitions) { + disks.initialize_volume_groups (); + } + + DiskInfo result = DiskInfo (); + + Disk[] physical_disks = {}; + Disk[] logical_disks = {}; + + foreach (unowned Distinst.Disk disk in disks.list ()) { + // Skip root disk or live disk + if (disk.contains_mount ("/", disks) || disk.contains_mount ("/cdrom", disks)) { + continue; + } + + Partition[] partitions = {}; + + if (get_partitions) { + foreach (unowned Distinst.Partition part in disk.list_partitions ()) { + string lvm_vg = (part.get_file_system () == Distinst.FileSystem.LVM) + ? string_from_utf8 (part.get_current_lvm_volume_group ()) + : ""; + + partitions += Partition () { + device_path = string_from_utf8 (part.get_device_path ()), + filesystem = part.get_file_system (), + start_sector = part.get_start_sector (), + end_sector = part.get_end_sector (), + sectors_used = part.sectors_used (disk.get_sector_size ()), + current_lvm_volume_group = lvm_vg + }; + } + } + + string model = string_from_utf8 (disk.get_model ()); + string name = model.length == 0 ? string_from_utf8 (disk.get_serial ()).replace ("_", " ") : model; + + physical_disks += Disk () { + name = name, + device_path = string_from_utf8 (disk.get_device_path ()), + sectors = disk.get_sectors (), + sector_size = disk.get_sector_size (), + rotational = disk.is_rotational (), + removable = disk.is_removable (), + partitions = partitions + }; + } + + if (get_partitions) { + foreach (unowned Distinst.LvmDevice disk in disks.list_logical ()) { + Partition[] partitions = {}; + + foreach (unowned Distinst.Partition part in disk.list_partitions ()) { + string lvm_vg = (part.get_file_system () == Distinst.FileSystem.LVM) + ? string_from_utf8 (part.get_current_lvm_volume_group ()) + : ""; + + partitions += Partition () { + device_path = string_from_utf8 (part.get_device_path ()), + filesystem = part.get_file_system (), + start_sector = part.get_start_sector (), + end_sector = part.get_end_sector (), + sectors_used = part.sectors_used (disk.get_sector_size ()), + current_lvm_volume_group = lvm_vg + }; + } + + logical_disks += Disk () { + name = string_from_utf8 (disk.get_model ()), + device_path = string_from_utf8 (disk.get_device_path ()), + sectors = disk.get_sectors (), + sector_size = disk.get_sector_size (), + partitions = partitions + }; + } + } + + result.physical_disks = physical_disks; + result.logical_disks = logical_disks; + return result; + } + + public int decrypt_partition (string path, string pv, string password) throws GLib.Error { + return disks.decrypt_partition (path, Distinst.LvmEncryption () { + physical_volume = pv, + password = password, + keydata = null + }); + } + + public Disk get_logical_device (string pv) throws GLib.Error { + unowned Distinst.LvmDevice disk = disks.get_logical_device (pv); + if (disk == null) { + throw new GLib.IOError.NOT_FOUND ("Couldn't find a logical device with that name"); + } + + Partition[] partitions = {}; + + foreach (unowned Distinst.Partition part in disk.list_partitions ()) { + string lvm_vg = (part.get_file_system () == Distinst.FileSystem.LVM) + ? string_from_utf8 (part.get_current_lvm_volume_group ()) + : ""; + + partitions += Partition () { + device_path = string_from_utf8 (part.get_device_path ()), + filesystem = part.get_file_system (), + start_sector = part.get_start_sector (), + end_sector = part.get_end_sector (), + sectors_used = part.sectors_used (disk.get_sector_size ()), + current_lvm_volume_group = lvm_vg + }; + } + + return Disk () { + name = string_from_utf8 (disk.get_model ()), + device_path = string_from_utf8 (disk.get_device_path ()), + sectors = disk.get_sectors (), + sector_size = disk.get_sector_size (), + partitions = partitions + }; + } + + public void install_with_default_disk_layout (InstallConfig config, string disk, bool encrypt, string encryption_password) throws GLib.Error { + var disks = new Distinst.Disks (); + default_disk_configuration (disks, disk, encrypt ? encryption_password : null); + + install (config, (owned) disks); + } + + public void install_with_custom_disk_layout (InstallConfig config, Mount[] disk_config, LuksCredentials[] credentials) throws GLib.Error { + var disks = new Distinst.Disks (); + custom_disk_configuration (disks, disk_config, credentials); + + install (config, (owned) disks); + } + + private void install (InstallConfig config, owned Distinst.Disks disks) { + var installer = new Distinst.Installer (); + installer.on_error ((error) => on_error (error)); + installer.on_status ((status) => on_status (status)); + + var distinst_config = Distinst.Config (); + distinst_config.flags = config.flags; + distinst_config.hostname = config.hostname; + + var casper = casper_dir (); + distinst_config.remove = GLib.Path.build_filename (casper, "filesystem.manifest-remove"); + distinst_config.squashfs = GLib.Path.build_filename (casper, "filesystem.squashfs"); + + debug ("language: %s\n", config.lang); + distinst_config.lang = config.lang; + + distinst_config.keyboard_layout = config.keyboard_layout; + distinst_config.keyboard_model = null; + distinst_config.keyboard_variant = config.keyboard_variant == "" ? null : config.keyboard_variant; + + new Thread (null, () => { + installer.install ((owned) disks, distinst_config); + return null; + }); + } + + private string casper_dir () { + const string CDROM = "/cdrom"; + + try { + var cdrom_dir = File.new_for_path (CDROM); + var iter = cdrom_dir.enumerate_children (FileAttribute.STANDARD_NAME, 0); + + FileInfo info; + while ((info = iter.next_file ()) != null) { + unowned string name = info.get_name (); + if (name.has_prefix ("casper")) { + return GLib.Path.build_filename (CDROM, name); + } + } + } catch (GLib.Error e) { + critical ("failed to find casper dir automatically: %s\n", e.message); + } + + return GLib.Path.build_filename (CDROM, "casper"); + } + + private void default_disk_configuration (Distinst.Disks disks, string disk_path, string? encryption_password) throws GLib.IOError { + var encrypted_vg = Distinst.generate_unique_id ("cryptdata"); + var root_vg = Distinst.generate_unique_id ("data"); + if (encrypted_vg == null || root_vg == null) { + throw new GLib.IOError.FAILED ("Unable to generate unique volume group IDs"); + } + + Distinst.LvmEncryption? encryption; + if (encryption_password != null) { + debug ("encrypting"); + encryption = Distinst.LvmEncryption () { + physical_volume = encrypted_vg, + password = encryption_password, + keydata = null + }; + } else { + debug ("not encrypting"); + encryption = null; + } + + debug ("disk: %s\n", disk_path); + var disk = new Distinst.Disk (disk_path); + if (disk == null) { + throw new GLib.IOError.FAILED ("Could not find %s", disk_path); + } + + var bootloader = Distinst.bootloader_detect (); + + var start_sector = Distinst.Sector () { + flag = Distinst.SectorKind.START, + value = 0 + }; + + // 256 MiB is the minimum distinst ESP partition size, so this is 256 MiB in MB plus a bit + // extra for safety + var efi_sector = Distinst.Sector () { + flag = Distinst.SectorKind.MEGABYTE, + value = 278 + }; + + // 512MB /boot partition that's created if we're doing encryption + var boot_sector = Distinst.Sector () { + flag = Distinst.SectorKind.MEGABYTE, + value = efi_sector.value + 512 + }; + + // 4GB swap partition at the end + var swap_sector = Distinst.Sector () { + flag = Distinst.SectorKind.MEGABYTE_FROM_END, + value = 4096 + }; + + var end_sector = Distinst.Sector () { + flag = Distinst.SectorKind.END, + value = 0 + }; + + // Prepares a new partition table. + int result = disk.mklabel (bootloader); + + if (result != 0) { + throw new GLib.IOError.FAILED ("Unable to write partition table to %s", disk_path); + } + + var start = disk.get_sector (ref start_sector); + var end = disk.get_sector (ref boot_sector); + + switch (bootloader) { + case Distinst.PartitionTable.MSDOS: + // This is used to ensure LVM installs will work with BIOS + result = disk.add_partition ( + new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.EXT4) + .partition_type (Distinst.PartitionType.PRIMARY) + .flag (Distinst.PartitionFlag.BOOT) + .mount ("/boot") + ); + + if (result != 0) { + throw new GLib.IOError.FAILED ("Unable to add boot partition to %s", disk_path); + } + + break; + case Distinst.PartitionTable.GPT: + end = disk.get_sector (ref efi_sector); + + // A FAT32 partition is required for EFI installs + result = disk.add_partition ( + new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.FAT32) + .partition_type (Distinst.PartitionType.PRIMARY) + .flag (Distinst.PartitionFlag.ESP) + .mount ("/boot/efi") + ); + + if (result != 0) { + throw new GLib.IOError.FAILED ("Unable to add EFI partition to %s", disk_path); + } + + // If we're encrypting, we need an unencrypted partition to store kernels and initramfs images + if (encryption != null) { + start = disk.get_sector (ref efi_sector); + end = disk.get_sector (ref boot_sector); + + result = disk.add_partition ( + new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.EXT4) + .partition_type (Distinst.PartitionType.PRIMARY) + .mount ("/boot") + ); + + if (result != 0) { + throw new GLib.IOError.FAILED ("unable to add /boot partition to %s", disk_path); + } + } + + break; + } + + // Start the LVM from the end of the /boot partition if we have encryption enabled + if (encryption != null) { + start = disk.get_sector (ref boot_sector); + } else { + start = disk.get_sector (ref efi_sector); + } + + end = disk.get_sector (ref end_sector); + + result = disk.add_partition ( + new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.LVM) + .partition_type (Distinst.PartitionType.PRIMARY) + .logical_volume (root_vg, encryption) + ); + + if (result != 0) { + throw new GLib.IOError.FAILED ("Unable to add LVM partition to %s", disk_path); + } + + disks.push ((owned) disk); + + result = disks.initialize_volume_groups (); + + if (result != 0) { + throw new GLib.IOError.FAILED ("Unable to initialize volume groups on %s", disk_path); + } + + unowned Distinst.LvmDevice lvm_device = disks.get_logical_device (root_vg); + + if (lvm_device == null) { + throw new GLib.IOError.FAILED ("Unable to find '%s' volume group on %s", root_vg, disk_path); + } + + start = lvm_device.get_sector (ref start_sector); + end = lvm_device.get_sector (ref swap_sector); + + result = lvm_device.add_partition ( + new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.EXT4) + .name ("root") + .mount ("/") + ); + + if (result != 0) { + throw new GLib.IOError.FAILED ("Unable to add / partition to LVM on %s", disk_path); + } + + start = lvm_device.get_sector (ref swap_sector); + end = lvm_device.get_sector (ref end_sector); + + result = lvm_device.add_partition ( + new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.SWAP) + .name ("swap") + ); + + if (result != 0) { + throw new GLib.IOError.FAILED ("Unable to add swap partition to LVM on %s", disk_path); + } + } + + private void custom_disk_configuration (Distinst.Disks disks, Mount[] mounts, LuksCredentials[] credentials) throws GLib.IOError { + Mount[] lvm_devices = {}; + + foreach (Mount m in mounts) { + if (m.is_lvm ()) { + lvm_devices += m; + } else { + unowned Distinst.Disk disk = disks.get_physical_device (m.parent_disk); + if (disk == null) { + var new_disk = new Distinst.Disk (m.parent_disk); + if (new_disk == null) { + throw new GLib.IOError.FAILED ("Could not find physical device: '%s'", m.parent_disk); + } + + disks.push ((owned) new_disk); + disk = disks.get_physical_device (m.parent_disk); + } + + unowned Distinst.Partition partition = disk.get_partition_by_path (m.partition_path); + + if (partition == null) { + throw new GLib.IOError.FAILED ("Could not find %s", m.partition_path); + } + + if (m.mount_point == "/boot/efi") { + if (m.is_valid_boot_mount ()) { + if (m.should_format ()) { + partition.format_with (m.filesystem); + } + + partition.set_mount (m.mount_point); + partition.set_flags ({ Distinst.PartitionFlag.ESP }); + } else { + throw new GLib.IOError.FAILED ("Unreachable code path -- EFI partition is invalid"); + } + } else { + if (m.filesystem != Distinst.FileSystem.SWAP) { + partition.set_mount (m.mount_point); + } + + if (m.mount_point == "/boot") { + partition.set_flags ({ Distinst.PartitionFlag.BOOT }); + } + + if (m.should_format ()) { + partition.format_with (m.filesystem); + } + } + } + } + + disks.initialize_volume_groups (); + + foreach (LuksCredentials cred in credentials) { + disks.decrypt_partition (cred.device, Distinst.LvmEncryption () { + physical_volume = cred.pv, + password = cred.password, + keydata = null + }); + } + + foreach (Mount m in lvm_devices) { + var vg = m.parent_disk.offset (12); + unowned Distinst.LvmDevice disk = disks.get_logical_device (vg); + if (disk == null) { + throw new GLib.IOError.FAILED ("Could not find %s", vg); + } + + unowned Distinst.Partition partition = disk.get_partition_by_path (m.partition_path); + + if (partition == null) { + throw new GLib.IOError.FAILED ("could not find %s", m.partition_path); + } + + if (m.filesystem != Distinst.FileSystem.SWAP) { + partition.set_mount (m.mount_point); + } + + if (m.should_format ()) { + partition.format_and_keep_name (m.filesystem); + } + } + } + + private static string string_from_utf8 (uint8[] input) { + var builder = new GLib.StringBuilder.sized (input.length); + builder.append_len ((string) input, input.length); + return (owned) builder.str; + } +} + +private void on_bus_acquired (GLib.DBusConnection connection, string name) { + try { + connection.register_object ("/io/elementary/InstallerDaemon", new InstallerDaemon.Daemon ()); + } catch (GLib.Error e) { + critical ("Unable to register the object: %s", e.message); + } +} + +public static int main (string[] args) { + loop = new GLib.MainLoop (null, false); + + var owner_id = GLib.Bus.own_name ( + GLib.BusType.SYSTEM, + "io.elementary.InstallerDaemon", + GLib.BusNameOwnerFlags.NONE, + on_bus_acquired, + () => { }, + () => { loop.quit (); } + ); + + loop.run (); + + GLib.Bus.unown_name (owner_id); + + return 0; +} diff --git a/daemon/io.elementary.InstallerDaemon.conf b/daemon/io.elementary.InstallerDaemon.conf new file mode 100644 index 000000000..99c0628df --- /dev/null +++ b/daemon/io.elementary.InstallerDaemon.conf @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/daemon/io.elementary.InstallerDaemon.service b/daemon/io.elementary.InstallerDaemon.service new file mode 100644 index 000000000..9260b984a --- /dev/null +++ b/daemon/io.elementary.InstallerDaemon.service @@ -0,0 +1,11 @@ +[Unit] +Description=elementary Installer Backend + +[Service] +Type=dbus +Restart=always +BusName=io.elementary.InstallerDaemon +ExecStart=io.elementary.installer-daemon + +[Install] +WantedBy=multi-user.target diff --git a/daemon/meson.build b/daemon/meson.build new file mode 100644 index 000000000..f041264cc --- /dev/null +++ b/daemon/meson.build @@ -0,0 +1,25 @@ +vala_files = [ + 'Daemon.vala', + common_files +] + +daemon_dependencies = [ + distinst_dep, + gee_dep, + glib_dep, + gio_dep, + gobject_dep, +] + +systemdunitdir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir') + +install_data( + 'io.elementary.InstallerDaemon.service', + install_dir: systemdunitdir +) + +install_data('io.elementary.InstallerDaemon.conf', install_dir : get_option('datadir') / 'dbus-1' / 'system.d') + +executable(meson.project_name() + '-daemon', vala_files, + dependencies : daemon_dependencies, + install: true) diff --git a/meson.build b/meson.build index e7a4e5297..28a5fc17b 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,7 @@ glib_dep = dependency('glib-2.0') gobject_dep = dependency('gobject-2.0') gtk_dep = dependency('gtk+-3.0') gee_dep = dependency('gee-0.8') +gio_dep = dependency('gio-2.0') granite_dep = dependency('granite', version: '>=0.5') handy_dep = dependency('libhandy-1', version: '>=0.90.0') json_glib_dep = dependency('json-glib-1.0') @@ -19,21 +20,7 @@ xml2_dep = dependency('libxml-2.0') gnome_keyboard_dep = dependency('libgnomekbd') gnome_keyboard_ui_dep = meson.get_compiler('c').find_library('gnomekbdui') pwquality_dep = dependency('pwquality') - -dependencies = [ - distinst_dep, - gee_dep, - glib_dep, - gnome_keyboard_dep, - gnome_keyboard_ui_dep, - gobject_dep, - granite_dep, - gtk_dep, - handy_dep, - json_glib_dep, - pwquality_dep, - xml2_dep -] +systemd_dep = dependency('systemd') asresources = gnome.compile_resources( 'as-resources', 'data/' + meson.project_name() + '.gresource.xml', @@ -42,5 +29,7 @@ asresources = gnome.compile_resources( ) subdir('po') +subdir('common') +subdir('daemon') subdir('src') subdir('data') diff --git a/src/Helpers/InstallerDaemon.vala b/src/Helpers/InstallerDaemon.vala new file mode 100644 index 000000000..462ca3d37 --- /dev/null +++ b/src/Helpers/InstallerDaemon.vala @@ -0,0 +1,128 @@ +/* + * Copyright 2021 elementary, Inc. + * + * 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 3 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, see . + */ + +public class Installer.Daemon { + // Wait up to 60 seconds for DBus calls to timeout. Some of the Distinst disk probe operations seem to take around 30 seconds + private const int DBUS_TIMEOUT_MSEC = 60 * 1000; + + [DBus (name = "io.elementary.InstallerDaemon")] + private interface InstallerInterface : GLib.DBusProxy { + public signal void on_error (Distinst.Error error); + public signal void on_status (Distinst.Status status); + public signal void on_log_message (Distinst.LogLevel level, string message); + + public abstract Distinst.PartitionTable bootloader_detect () throws GLib.Error; + + public async abstract InstallerDaemon.DiskInfo get_disks (bool get_partitions = false) throws GLib.Error; + public async abstract int decrypt_partition (string path, string pv, string password) throws GLib.Error; + public async abstract InstallerDaemon.Disk get_logical_device (string pv) throws GLib.Error; + public async abstract void install_with_default_disk_layout (InstallerDaemon.InstallConfig config, string disk, bool encrypt, string encryption_password) throws GLib.Error; + public async abstract void install_with_custom_disk_layout (InstallerDaemon.InstallConfig config, InstallerDaemon.Mount[] disk_config, InstallerDaemon.LuksCredentials[] luks) throws GLib.Error; + } + + public signal void on_error (Distinst.Error error); + public signal void on_status (Distinst.Status status); + public signal void on_log_message (Distinst.LogLevel level, string message); + + private InstallerInterface daemon; + + private Daemon () { + try { + daemon = Bus.get_proxy_sync (BusType.SYSTEM, "io.elementary.InstallerDaemon", "/io/elementary/InstallerDaemon"); + } catch (Error e) { + critical ("Unable to connect to daemon: %s", e.message); + return; + } + + daemon.g_default_timeout = DBUS_TIMEOUT_MSEC; + + daemon.on_error.connect ((error) => on_error (error)); + daemon.on_status.connect ((status) => on_status (status)); + daemon.on_log_message.connect ((level, message) => on_log_message (level, message)); + } + + public Distinst.PartitionTable bootloader_detect () { + if (daemon == null) { + return fallback_bootloader_detect (); + } + + try { + return daemon.bootloader_detect (); + } catch (Error e) { + return fallback_bootloader_detect (); + } + } + + private Distinst.PartitionTable fallback_bootloader_detect () { + var efi_file = GLib.File.new_for_path ("/sys/firmware/efi"); + if (efi_file.query_exists ()) { + return Distinst.PartitionTable.GPT; + } else { + return Distinst.PartitionTable.MSDOS; + } + } + + public async InstallerDaemon.DiskInfo get_disks (bool get_partitions = false) throws GLib.Error { + if (daemon == null) { + throw new GLib.IOError.FAILED ("Not connected to installer daemon"); + } + + return yield daemon.get_disks (get_partitions); + } + + public async int decrypt_partition (string path, string pv, string password) throws GLib.Error { + if (daemon == null) { + throw new GLib.IOError.FAILED ("Not connected to installer daemon"); + } + + return yield daemon.decrypt_partition (path, pv, password); + } + + public async InstallerDaemon.Disk get_logical_device (string pv) throws GLib.Error { + if (daemon == null) { + throw new GLib.IOError.FAILED ("Not connected to installer daemon"); + } + + return yield daemon.get_logical_device (pv); + } + + public async void install_with_default_disk_layout (InstallerDaemon.InstallConfig config, string disk, bool encrypt, string encryption_password) throws GLib.Error { + if (daemon == null) { + throw new GLib.IOError.FAILED ("Not connected to installer daemon"); + } + + yield daemon.install_with_default_disk_layout (config, disk, encrypt, encryption_password); + } + + public async void install_with_custom_disk_layout (InstallerDaemon.InstallConfig config, InstallerDaemon.Mount[] disk_config, InstallerDaemon.LuksCredentials[] luks) throws GLib.Error { + if (daemon == null) { + throw new GLib.IOError.FAILED ("Not connected to installer daemon"); + } + + yield daemon.install_with_custom_disk_layout (config, disk_config, luks); + } + + private static Daemon? _instance = null; + public static unowned Daemon get_default () { + if (_instance != null) { + return _instance; + } + + _instance = new Daemon (); + return _instance; + } +} diff --git a/src/Helpers/LogHelper.vala b/src/Helpers/LogHelper.vala index 745f5a15d..00677e1db 100644 --- a/src/Helpers/LogHelper.vala +++ b/src/Helpers/LogHelper.vala @@ -48,21 +48,13 @@ public class LogHelper : GLib.Object { construct { buffer = new Gtk.TextBuffer (null); buffer.text = ""; - if (Distinst.log (log_func) != 0) { - log_func (Distinst.LogLevel.ERROR, _("Unable to set the Distinst log callback")); - } else { - log_func (Distinst.LogLevel.INFO, _("Starting installation")); - } } - private void log_func (Distinst.LogLevel level, string message) { + public void log_func (Distinst.LogLevel level, string message) { stdout.printf ("log: %s: %s\n", level_name (level), message); - Idle.add (() => { - Gtk.TextIter end_iter; - buffer.get_end_iter (out end_iter); - string new_line = "\n" + level_name (level) + ": " + message; - buffer.insert (ref end_iter, new_line, new_line.length); - return GLib.Source.REMOVE; - }); + Gtk.TextIter end_iter; + buffer.get_end_iter (out end_iter); + string new_line = "\n" + level_name (level) + ": " + message; + buffer.insert (ref end_iter, new_line, new_line.length); } } diff --git a/src/MainWindow.vala b/src/MainWindow.vala index a495d537a..1cb5de2a7 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -32,7 +32,6 @@ public class Installer.MainWindow : Hdy.Window { private ErrorView error_view; private bool check_ignored = false; - private uint64 minimum_disk_size; public MainWindow () { Object ( @@ -57,8 +56,6 @@ public class Installer.MainWindow : Hdy.Window { add (stack); - minimum_disk_size = Distinst.minimum_disk_size (5000000000); - language_view.next_step.connect (() => load_keyboard_view ()); } @@ -109,7 +106,7 @@ public class Installer.MainWindow : Hdy.Window { check_view.destroy (); } - check_view = new Installer.CheckView (minimum_disk_size); + check_view = new Installer.CheckView (); stack.add (check_view); check_view.status_changed.connect ((met_requirements) => { @@ -160,7 +157,7 @@ public class Installer.MainWindow : Hdy.Window { disk_view.previous_view = try_install_view; stack.add (disk_view); stack.visible_child = disk_view; - disk_view.load.begin (minimum_disk_size); + disk_view.load.begin (CheckView.MINIMUM_SPACE); disk_view.cancel.connect (() => { stack.visible_child = try_install_view; @@ -174,7 +171,7 @@ public class Installer.MainWindow : Hdy.Window { partitioning_view.destroy (); } - partitioning_view = new PartitioningView (minimum_disk_size); + partitioning_view = new PartitioningView (CheckView.MINIMUM_SPACE); partitioning_view.previous_view = try_install_view; stack.add (partitioning_view); stack.visible_child = partitioning_view; diff --git a/src/Objects/Configuration.vala b/src/Objects/Configuration.vala index 5e23dc0d4..cecc3e66c 100644 --- a/src/Objects/Configuration.vala +++ b/src/Objects/Configuration.vala @@ -35,5 +35,5 @@ public class Configuration : GLib.Object { public string? encryption_password { get; set; default = null; } public string disk { get; set; } public Gee.ArrayList? mounts { get; set; default = null; } - public Gee.ArrayList? luks { get; set; default = null; } + public Gee.ArrayList? luks { get; set; default = null; } } diff --git a/src/Objects/Mount.vala b/src/Objects/Mount.vala index 8e58374be..6896f0ac6 100644 --- a/src/Objects/Mount.vala +++ b/src/Objects/Mount.vala @@ -24,18 +24,11 @@ public class Installer.Mount { public string mount_point; public uint64 sectors; public Distinst.FileSystem filesystem; - public Flags flags; + public InstallerDaemon.MountFlags flags; public PartitionMenu menu; - [Flags] - public enum Flags { - FORMAT = 1, - LVM = 2, - LVM_ON_LUKS = 4 - } - public Mount (string partition, string parent_disk, string mount, - uint64 sectors, Flags flags, Distinst.FileSystem fs, + uint64 sectors, InstallerDaemon.MountFlags flags, Distinst.FileSystem fs, PartitionMenu menu) { filesystem = fs; mount_point = mount; @@ -58,22 +51,10 @@ public class Installer.Mount { } public bool is_lvm () { - return Flags.LVM in flags; + return InstallerDaemon.MountFlags.LVM in flags; } public bool should_format () { - return Flags.FORMAT in flags; - } -} - -public class Installer.LuksCredentials { - public string device; - public string pv; - public string password; - - public LuksCredentials (string device, string pv, string password) { - this.device = device; - this.pv = pv; - this.password = password; + return InstallerDaemon.MountFlags.FORMAT in flags; } } diff --git a/src/Utils.vala b/src/Utils.vala index 7027c444d..db2e267a1 100644 --- a/src/Utils.vala +++ b/src/Utils.vala @@ -19,30 +19,13 @@ */ namespace Utils { - public string string_from_utf8 (uint8[] input) { - var builder = new GLib.StringBuilder.sized (input.length); - builder.append_len ((string) input, input.length); - return (owned) builder.str; - } - - private struct OsRelease { - public string pretty_name; - - public static OsRelease new () { - return OsRelease () { - pretty_name = string_from_utf8 (Distinst.get_os_pretty_name ()) - }; - } - } - - private static OsRelease? os_release; - + private static string? pretty_name; private static string get_pretty_name () { - if (os_release == null) { - os_release = OsRelease.new (); + if (pretty_name == null) { + pretty_name = GLib.Environment.get_os_info (GLib.OsInfoKey.PRETTY_NAME); } - return os_release.pretty_name; + return pretty_name; } public static void shutdown () { diff --git a/src/Views/CheckView.vala b/src/Views/CheckView.vala index a191c131a..c8a743648 100644 --- a/src/Views/CheckView.vala +++ b/src/Views/CheckView.vala @@ -38,7 +38,6 @@ public class Installer.CheckView : AbstractInstallerView { int frequency = 0; uint64 memory = 0; - public static uint64 minimum_disk_size; private UPower upower; enum State { @@ -52,8 +51,7 @@ public class Installer.CheckView : AbstractInstallerView { private Gtk.Button ignore_button; private Gtk.Stack stack; - public CheckView (uint64 size) { - minimum_disk_size = size; + public CheckView () { Object (cancellable: true); } @@ -94,10 +92,28 @@ public class Installer.CheckView : AbstractInstallerView { } private static bool get_has_enough_space () { - Distinst.Disks disks = Distinst.Disks.probe (); - foreach (unowned Distinst.Disk disk in disks.list ()) { - uint64 size = disk.get_sectors () * disk.get_sector_size (); - if (size > minimum_disk_size) { + var loop = new MainLoop (); + InstallerDaemon.DiskInfo? disks = null; + + Daemon.get_default ().get_disks.begin (false, (obj, res) => { + try { + disks = ((Daemon)obj).get_disks.end (res); + } catch (Error e) { + critical ("Unable to get disks list: %s", e.message); + } finally { + loop.quit (); + } + }); + + loop.run (); + + if (disks == null) { + return false; + } + + foreach (unowned InstallerDaemon.Disk disk in disks.physical_disks) { + uint64 size = disk.sectors * disk.sector_size; + if (size > MINIMUM_SPACE) { return true; } } diff --git a/src/Views/DiskView.vala b/src/Views/DiskView.vala index 0597060c9..12291f21c 100644 --- a/src/Views/DiskView.vala +++ b/src/Views/DiskView.vala @@ -103,48 +103,40 @@ public class Installer.DiskView : AbstractInstallerView { show_all (); } - // If possible, open devices in a different thread so that the interface stays awake. public async void load (uint64 minimum_disk_size) { DiskButton[] enabled_buttons = {}; DiskButton[] disabled_buttons = {}; - Distinst.Disks disks = Distinst.Disks.probe (); - foreach (unowned Distinst.Disk disk in disks.list ()) { - // Skip root disk or live disk - if (disk.contains_mount ("/", disks) || disk.contains_mount ("/cdrom", disks)) { - continue; - } + InstallerDaemon.DiskInfo? disks; + try { + disks = yield Daemon.get_default ().get_disks (); + } catch (Error e) { + critical ("Unable to get disks list: %s", e.message); + load_stack.set_visible_child_name ("disk"); + return; + } - var size = disk.get_sectors () * disk.get_sector_size (); + foreach (unowned InstallerDaemon.Disk disk in disks.physical_disks) { + var size = disk.sectors * disk.sector_size; // Drives are identifiable by whether they are rotational and/or removable. string icon_name = null; - if (disk.is_removable ()) { - if (disk.is_rotational ()) { + if (disk.removable) { + if (disk.rotational) { icon_name = "drive-harddisk-usb"; } else { icon_name = "drive-removable-media-usb"; } - } else if (disk.is_rotational ()) { + } else if (disk.rotational) { icon_name = "drive-harddisk-scsi"; } else { icon_name = "drive-harddisk-solidstate"; } - string label; - string model = Utils.string_from_utf8 (disk.get_model ()); - if (model.length == 0) { - label = Utils.string_from_utf8 (disk.get_serial ()).replace ("_", " "); - } else { - label = model; - } - - string path = Utils.string_from_utf8 (disk.get_device_path ()); - var disk_button = new DiskButton ( - label, + disk.name, icon_name, - path, + disk.device_path, size ); diff --git a/src/Views/PartitioningView.vala b/src/Views/PartitioningView.vala index 48d7a58e5..458202437 100644 --- a/src/Views/PartitioningView.vala +++ b/src/Views/PartitioningView.vala @@ -23,13 +23,13 @@ public class Installer.PartitioningView : AbstractInstallerView { private Gtk.Button next_button; private Gtk.Button modify_partitions_button; - private Distinst.Disks disks; private Gtk.Box disk_list; private Gtk.SizeGroup label_sizer; + private Gtk.Stack load_stack; private string required_description; public Gee.ArrayList mounts; - public Gee.ArrayList luks; + public Gee.ArrayList luks; public static uint64 minimum_disk_size; @@ -48,12 +48,12 @@ public class Installer.PartitioningView : AbstractInstallerView { construct { mounts = new Gee.ArrayList (); - luks = new Gee.ArrayList (); + luks = new Gee.ArrayList (); margin = 12; var base_description = _("Select which partitions to use across all drives. Selecting \"Format\" will erase ALL data on the selected partition."); - var bootloader = Distinst.bootloader_detect (); + var bootloader = Daemon.get_default ().bootloader_detect (); switch (bootloader) { case Distinst.PartitionTable.MSDOS: // Device is in BIOS mode, so we just require a root partition @@ -89,10 +89,32 @@ public class Installer.PartitioningView : AbstractInstallerView { disk_scroller.hscrollbar_policy = Gtk.PolicyType.NEVER; disk_scroller.add (disk_list); - content_area.attach (disk_scroller, 0, 0); + var load_spinner = new Gtk.Spinner (); + load_spinner.halign = Gtk.Align.CENTER; + load_spinner.valign = Gtk.Align.CENTER; + load_spinner.start (); + + var load_label = new Gtk.Label (_("Getting the current configuration…")); + load_label.get_style_context ().add_class ("h2"); + + var load_grid = new Gtk.Grid (); + load_grid.row_spacing = 12; + load_grid.expand = true; + load_grid.orientation = Gtk.Orientation.VERTICAL; + load_grid.valign = Gtk.Align.CENTER; + load_grid.halign = Gtk.Align.CENTER; + load_grid.add (load_spinner); + load_grid.add (load_label); + + load_stack = new Gtk.Stack (); + load_stack.transition_type = Gtk.StackTransitionType.CROSSFADE; + load_stack.add_named (load_grid, "loading"); + load_stack.add_named (disk_scroller, "disk"); + + content_area.attach (load_stack, 0, 0); content_area.attach (description, 0, 1); - load_disks (); + load_disks.begin (); modify_partitions_button = new Gtk.Button.with_label (_("Modify Partitions…")); modify_partitions_button.clicked.connect (() => open_partition_editor ()); @@ -115,40 +137,45 @@ public class Installer.PartitioningView : AbstractInstallerView { show_all (); } - private void load_disks () { - disks = Distinst.Disks.probe (); - disks.initialize_volume_groups (); - label_sizer = new Gtk.SizeGroup (Gtk.SizeGroupMode.BOTH); + private async void load_disks () { + load_stack.set_visible_child_name ("loading"); - foreach (unowned Distinst.Disk disk in disks.list ()) { - // Skip root disk or live disk - if (disk.contains_mount ("/", disks) || disk.contains_mount ("/cdrom", disks)) { - continue; - } + label_sizer = new Gtk.SizeGroup (Gtk.SizeGroupMode.BOTH); - var sector_size = disk.get_sector_size (); - var size = disk.get_sectors () * sector_size; + InstallerDaemon.DiskInfo? disks = null; + try { + disks = yield Daemon.get_default ().get_disks (true); + } catch (Error e) { + critical ("Unable to get disks: %s", e.message); + load_stack.set_visible_child_name ("disk"); + return; + } - string path = Utils.string_from_utf8 (disk.get_device_path ()); + foreach (unowned InstallerDaemon.Disk disk in disks.physical_disks) { + var sector_size = disk.sector_size; + var size = disk.sectors * sector_size; - string model = Utils.string_from_utf8 (disk.get_model ()); + unowned string path = disk.device_path; var partitions = new Gee.ArrayList (); - foreach (unowned Distinst.Partition part in disk.list_partitions ()) { - var partition = new PartitionBar (part, path, sector_size, false, this.set_mount, this.unset_mount, this.mount_is_set, this.decrypt); + foreach (unowned InstallerDaemon.Partition part in disk.partitions) { + var partition = new PartitionBar (part, path, sector_size, false, this.set_mount, this.unset_mount, this.mount_is_set); + partition.decrypted.connect (on_partition_decrypted); partitions.add (partition); } - var disk_bar = new DiskBar (model, path, size, (owned) partitions); + var disk_bar = new DiskBar (disk.name, path, size, (owned) partitions); label_sizer.add_widget (disk_bar.label); disk_list.pack_start (disk_bar); } - foreach (unowned Distinst.LvmDevice disk in disks.list_logical ()) { + foreach (unowned InstallerDaemon.Disk disk in disks.logical_disks) { add_logical_disk (disk); } disk_list.show_all (); + + load_stack.set_visible_child_name ("disk"); } private void open_partition_editor () { @@ -173,24 +200,23 @@ public class Installer.PartitioningView : AbstractInstallerView { mounts.clear (); luks.clear (); next_button.sensitive = false; - load_disks (); + load_disks.begin (); } - private void add_logical_disk (Distinst.LvmDevice disk) { - var sector_size = disk.get_sector_size (); - var size = disk.get_sectors () * sector_size; - - string path = Utils.string_from_utf8 (disk.get_device_path ()); + private void add_logical_disk (InstallerDaemon.Disk disk) { + var sector_size = disk.sector_size; + var size = disk.sectors * sector_size; - string model = Utils.string_from_utf8 (disk.get_model ()); + unowned string path = disk.device_path; var partitions = new Gee.ArrayList (); - foreach (unowned Distinst.Partition part in disk.list_partitions ()) { - var partition = new PartitionBar (part, path, sector_size, true, this.set_mount, this.unset_mount, this.mount_is_set, this.decrypt); + foreach (unowned InstallerDaemon.Partition part in disk.partitions) { + var partition = new PartitionBar (part, path, sector_size, true, this.set_mount, this.unset_mount, this.mount_is_set); + partition.decrypted.connect (on_partition_decrypted); partitions.add (partition); } - var disk_bar = new DiskBar (model, path, size, (owned) partitions); + var disk_bar = new DiskBar (disk.name, path, size, (owned) partitions); label_sizer.add_widget (disk_bar.label); disk_list.pack_start (disk_bar); } @@ -199,7 +225,7 @@ public class Installer.PartitioningView : AbstractInstallerView { uint8 flags = 0; uint8 required = Defined.ROOT; - var bootloader = Distinst.bootloader_detect (); + var bootloader = Daemon.get_default ().bootloader_detect (); switch (bootloader) { case Distinst.PartitionTable.MSDOS: break; @@ -230,42 +256,16 @@ public class Installer.PartitioningView : AbstractInstallerView { next_button.sensitive = required in flags; } - private void decrypt (string device, string pv, string password, DecryptMenu menu) { - int result = disks.decrypt_partition (device, Distinst.LvmEncryption () { - physical_volume = pv, - password = password, - keydata = null - }); - - switch (result) { - case 0: - unowned Distinst.LvmDevice disk = disks.get_logical_device (pv); + private void on_partition_decrypted (InstallerDaemon.LuksCredentials credentials) { + luks.add (credentials); + Daemon.get_default ().get_logical_device.begin (credentials.pv, (obj, res) => { + try { + var disk = ((Daemon)obj).get_logical_device.end (res); add_logical_disk (disk); - menu.set_decrypted (pv); - luks.add (new LuksCredentials (device, pv, password)); - break; - case 1: - debug ("decrypt_partition result is 1"); - break; - case 2: - debug ("decrypt: input was not valid UTF-8"); - break; - case 3: - debug ("decrypt: either a password or keydata string must be supplied"); - break; - case 4: - debug ("decrypt: unable to decrypt partition (possibly invalid password)"); - break; - case 5: - debug ("decrypt: the decrypted partition does not have a LVM volume on it"); - break; - case 6: - debug ("decrypt: unable to locate LUKS partition at %s", device); - break; - default: - critical ("decrypt: unhandled error value: %d", result); - break; - } + } catch (Error e) { + critical ("Unable to get logical device: %s", e.message); + } + }); } private void set_mount (Mount mount) throws GLib.Error { diff --git a/src/Views/ProgressView.vala b/src/Views/ProgressView.vala index 4311f0f1a..b78ed4c7c 100644 --- a/src/Views/ProgressView.vala +++ b/src/Views/ProgressView.vala @@ -76,27 +76,6 @@ public class ProgressView : AbstractInstallerView { return terminal_view.buffer.text; } - private string casper_dir () { - const string CDROM = "/cdrom"; - - try { - var cdrom_dir = File.new_for_path (CDROM); - var iter = cdrom_dir.enumerate_children (FileAttribute.STANDARD_NAME, 0); - - FileInfo info; - while ((info = iter.next_file ()) != null) { - unowned string name = info.get_name (); - if (name.has_prefix ("casper")) { - return GLib.Path.build_filename (CDROM, name); - } - } - } catch (GLib.Error e) { - critical ("failed to find casper dir automatically: %s\n", e.message); - } - - return GLib.Path.build_filename (CDROM, "casper"); - } - public void start_installation () { if (Installer.App.test_mode) { new Thread (null, () => { @@ -111,20 +90,18 @@ public class ProgressView : AbstractInstallerView { } } - private async void real_installation () { - var installer = new Distinst.Installer (); - installer.on_error (installation_error_callback); - installer.on_status (installation_status_callback); + public async void real_installation () { + unowned LogHelper log_helper = LogHelper.get_default (); + unowned Installer.Daemon daemon = Installer.Daemon.get_default (); + daemon.on_error.connect (installation_error_callback); + daemon.on_status.connect (installation_status_callback); + daemon.on_log_message.connect (log_helper.log_func); - var config = Distinst.Config (); + var config = InstallerDaemon.InstallConfig (); config.flags = Distinst.MODIFY_BOOT_ORDER; config.hostname = "elementary-os"; config.lang = "en_US.UTF-8"; - var casper = casper_dir (); - config.remove = GLib.Path.build_filename (casper, "filesystem.manifest-remove"); - config.squashfs = GLib.Path.build_filename (casper, "filesystem.squashfs"); - unowned Configuration current_config = Configuration.get_default (); debug ("language: %s\n", current_config.lang); @@ -142,312 +119,45 @@ public class ProgressView : AbstractInstallerView { } config.keyboard_layout = current_config.keyboard_layout; - config.keyboard_model = null; - config.keyboard_variant = current_config.keyboard_variant; + config.keyboard_variant = current_config.keyboard_variant ?? ""; - var disks = new Distinst.Disks (); if (current_config.mounts == null) { - default_disk_configuration (disks); - } else { - custom_disk_configuration (disks); - } - - new Thread (null, () => { - installer.install ((owned) disks, config); - return null; - }); - } - - private void default_disk_configuration (Distinst.Disks disks) { - unowned Configuration current_config = Configuration.get_default (); - - var encrypted_vg = Distinst.generate_unique_id ("cryptdata"); - var root_vg = Distinst.generate_unique_id ("data"); - if (encrypted_vg == null || root_vg == null) { - critical ("unable to generate unique volume group IDs\n"); - on_error (); - return; - } - - Distinst.LvmEncryption? encryption; - if (current_config.encryption_password != null) { - debug ("encrypting"); - encryption = Distinst.LvmEncryption () { - physical_volume = encrypted_vg, - password = current_config.encryption_password, - keydata = null - }; - } else { - debug ("not encrypting"); - encryption = null; - } - - debug ("disk: %s\n", current_config.disk); - var disk = new Distinst.Disk (current_config.disk); - if (disk == null) { - critical ("could not find %s", current_config.disk); - on_error (); - return; - } - - var bootloader = Distinst.bootloader_detect (); - - var start_sector = Distinst.Sector () { - flag = Distinst.SectorKind.START, - value = 0 - }; - - // 256 MiB is the minimum distinst ESP partition size, so this is 256 MiB in MB plus a bit - // extra for safety - var efi_sector = Distinst.Sector () { - flag = Distinst.SectorKind.MEGABYTE, - value = 278 - }; - - // 512MB /boot partition that's created if we're doing encryption - var boot_sector = Distinst.Sector () { - flag = Distinst.SectorKind.MEGABYTE, - value = efi_sector.value + 512 - }; - - // 4GB swap partition at the end - var swap_sector = Distinst.Sector () { - flag = Distinst.SectorKind.MEGABYTE_FROM_END, - value = 4096 - }; - - var end_sector = Distinst.Sector () { - flag = Distinst.SectorKind.END, - value = 0 - }; - - // Prepares a new partition table. - int result = disk.mklabel (bootloader); - - if (result != 0) { - critical ("unable to write partition table to %s", current_config.disk); - on_error (); - return; - } - - var start = disk.get_sector (ref start_sector); - var end = disk.get_sector (ref boot_sector); - - switch (bootloader) { - case Distinst.PartitionTable.MSDOS: - // This is used to ensure LVM installs will work with BIOS - result = disk.add_partition ( - new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.EXT4) - .partition_type (Distinst.PartitionType.PRIMARY) - .flag (Distinst.PartitionFlag.BOOT) - .mount ("/boot") - ); - - if (result != 0) { - critical ("unable to add boot partition to %s", current_config.disk); - on_error (); - return; - } - - break; - case Distinst.PartitionTable.GPT: - end = disk.get_sector (ref efi_sector); - - // A FAT32 partition is required for EFI installs - result = disk.add_partition ( - new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.FAT32) - .partition_type (Distinst.PartitionType.PRIMARY) - .flag (Distinst.PartitionFlag.ESP) - .mount ("/boot/efi") + try { + yield daemon.install_with_default_disk_layout ( + config, + current_config.disk, + current_config.encryption_password != null, + current_config.encryption_password ?? "" ); - - if (result != 0) { - critical ("unable to add EFI partition to %s", current_config.disk); - on_error (); - return; - } - - // If we're encrypting, we need an unencrypted partition to store kernels and initramfs images - if (encryption != null) { - start = disk.get_sector (ref efi_sector); - end = disk.get_sector (ref boot_sector); - - result = disk.add_partition ( - new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.EXT4) - .partition_type (Distinst.PartitionType.PRIMARY) - .mount ("/boot") - ); - - if (result != 0) { - critical ("unable to add /boot partition to %s", current_config.disk); - on_error (); - return; - } - } - - break; - } - - // Start the LVM from the end of the /boot partition if we have encryption enabled - if (encryption != null) { - start = disk.get_sector (ref boot_sector); - } else { - start = disk.get_sector (ref efi_sector); - } - - end = disk.get_sector (ref end_sector); - - result = disk.add_partition ( - new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.LVM) - .partition_type (Distinst.PartitionType.PRIMARY) - .logical_volume (root_vg, encryption) - ); - - if (result != 0) { - critical ("unable to add lvm partition to %s", current_config.disk); - on_error (); - return; - } - - disks.push ((owned) disk); - - result = disks.initialize_volume_groups (); - - if (result != 0) { - critical ("unable to initialize volume groups on %s", current_config.disk); - on_error (); - return; - } - - unowned Distinst.LvmDevice lvm_device = disks.get_logical_device (root_vg); - - if (lvm_device == null) { - critical ("unable to find '%s' volume group on %s", root_vg, current_config.disk); - on_error (); - return; - } - - start = lvm_device.get_sector (ref start_sector); - end = lvm_device.get_sector (ref swap_sector); - - result = lvm_device.add_partition ( - new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.EXT4) - .name ("root") - .mount ("/") - ); - - if (result != 0) { - critical ("unable to add / partition to lvm on %s", current_config.disk); - on_error (); - return; - } - - start = lvm_device.get_sector (ref swap_sector); - end = lvm_device.get_sector (ref end_sector); - - result = lvm_device.add_partition ( - new Distinst.PartitionBuilder (start, end, Distinst.FileSystem.SWAP) - .name ("swap") - ); - - if (result != 0) { - critical ("unable to add swap partition to lvm on %s", current_config.disk); - on_error (); - return; - } - } - - private void custom_disk_configuration (Distinst.Disks disks) { - unowned Configuration config = Configuration.get_default (); - Installer.Mount[] lvm_devices = {}; - - foreach (Installer.Mount m in config.mounts) { - if (m.is_lvm ()) { - lvm_devices += m; - } else { - unowned Distinst.Disk disk = disks.get_physical_device (m.parent_disk); - if (disk == null) { - var new_disk = new Distinst.Disk (m.parent_disk); - if (new_disk == null) { - warning ("could not find physical device: '%s'\n", m.parent_disk); - on_error (); - return; - } - - disks.push ((owned) new_disk); - disk = disks.get_physical_device (m.parent_disk); - } - - unowned Distinst.Partition partition = disk.get_partition_by_path (m.partition_path); - - if (partition == null) { - warning ("could not find %s\n", m.partition_path); - on_error (); - return; - } - - if (m.mount_point == "/boot/efi") { - if (m.is_valid_boot_mount ()) { - if (m.should_format ()) { - partition.format_with (m.filesystem); - } - - partition.set_mount (m.mount_point); - partition.set_flags ({ Distinst.PartitionFlag.ESP }); - } else { - warning ("unreachable code path -- efi partition is invalid\n"); - on_error (); - return; - } - } else { - if (m.filesystem != Distinst.FileSystem.SWAP) { - partition.set_mount (m.mount_point); - } - - if (m.mount_point == "/boot") { - partition.set_flags ({ Distinst.PartitionFlag.BOOT }); - } - - if (m.should_format ()) { - partition.format_with (m.filesystem); - } - } - } - } - - disks.initialize_volume_groups (); - - foreach (Installer.LuksCredentials cred in config.luks) { - disks.decrypt_partition (cred.device, Distinst.LvmEncryption () { - physical_volume = cred.pv, - password = cred.password, - keydata = null - }); - } - - foreach (Installer.Mount m in lvm_devices) { - var vg = m.parent_disk.offset (12); - unowned Distinst.LvmDevice disk = disks.get_logical_device (vg); - if (disk == null) { - warning ("could not find %s\n", vg); + } catch (Error e) { + log_helper.log_func (Distinst.LogLevel.ERROR, e.message); on_error (); - return; } - - unowned Distinst.Partition partition = disk.get_partition_by_path (m.partition_path); - - if (partition == null) { - warning ("could not find %s\n", m.partition_path); - on_error (); - return; + } else { + InstallerDaemon.Mount[] mounts = {}; + foreach (Installer.Mount m in current_config.mounts) { + mounts += InstallerDaemon.Mount () { + partition_path = m.partition_path, + parent_disk = m.parent_disk, + mount_point = m.mount_point, + sectors = m.sectors, + filesystem = m.filesystem, + flags = m.flags + }; } - if (m.filesystem != Distinst.FileSystem.SWAP) { - partition.set_mount (m.mount_point); + InstallerDaemon.LuksCredentials[] creds = {}; + foreach (InstallerDaemon.LuksCredentials? cred in current_config.luks) { + if (cred != null) { + creds += cred; + } } - if (m.should_format ()) { - partition.format_and_keep_name (m.filesystem); + try { + yield daemon.install_with_custom_disk_layout (config, mounts, creds); + } catch (Error e) { + log_helper.log_func (Distinst.LogLevel.ERROR, e.message); + on_error (); } } } diff --git a/src/Widgets/DecryptMenu.vala b/src/Widgets/DecryptMenu.vala index 060524b60..f1c1d1d0d 100644 --- a/src/Widgets/DecryptMenu.vala +++ b/src/Widgets/DecryptMenu.vala @@ -18,8 +18,6 @@ * Authored by: Michael Aaron Murphy */ -public delegate void DecryptFn (string path, string pv, string pass, Installer.DecryptMenu menu); - public class Installer.DecryptMenu: Gtk.Popover { private Gtk.Stack stack; @@ -28,15 +26,21 @@ public class Installer.DecryptMenu: Gtk.Popover { private Gtk.Entry pass_entry; private Gtk.Entry pv_entry; - public DecryptMenu (string device_path, DecryptFn decrypt) { + private string device_path; + + public signal void decrypted (InstallerDaemon.LuksCredentials creds); + + public DecryptMenu (string device_path) { + this.device_path = device_path; + stack = new Gtk.Stack (); stack.margin = 12; - create_decrypt_view (device_path, decrypt); + create_decrypt_view (); add (stack); stack.show_all (); } - private void create_decrypt_view (string device_path, DecryptFn decrypt) { + private void create_decrypt_view () { var image = new Gtk.Image.from_icon_name ("drive-harddisk", Gtk.IconSize.DIALOG); image.valign = Gtk.Align.START; @@ -77,7 +81,7 @@ public class Installer.DecryptMenu: Gtk.Popover { pass_entry.changed.connect (() => set_sensitivity ()); pass_entry.activate.connect (() => { if (entries_set ()) { - decrypt (device_path, pv_entry.get_text (), pass_entry.get_text (), this); + decrypt.begin (pv_entry.get_text ()); } }); @@ -90,7 +94,7 @@ public class Installer.DecryptMenu: Gtk.Popover { pv_entry.changed.connect (() => set_sensitivity ()); pv_entry.activate.connect (() => { if (entries_set ()) { - decrypt (device_path, pv_entry.get_text (), pass_entry.get_text (), this); + decrypt.begin (pv_entry.get_text ()); } }); @@ -99,7 +103,7 @@ public class Installer.DecryptMenu: Gtk.Popover { decrypt_button.sensitive = false; decrypt_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); decrypt_button.clicked.connect (() => { - decrypt (device_path, pv_entry.get_text (), pass_entry.get_text (), this); + decrypt.begin (pv_entry.get_text ()); }); decrypt_view = new Gtk.Grid (); @@ -118,6 +122,52 @@ public class Installer.DecryptMenu: Gtk.Popover { pass_entry.grab_focus_without_selecting (); } + private async void decrypt (string pv) { + unowned string password = pass_entry.get_text (); + + int result; + try { + result = yield Daemon.get_default ().decrypt_partition (device_path, pv_entry.get_text (), password); + } catch (Error e) { + critical ("Unable to decrypt partition: %s", e.message); + return; + } + + switch (result) { + case 0: + set_decrypted (pv); + var creds = InstallerDaemon.LuksCredentials () { + device = device_path, + pv = pv, + password = password + }; + + decrypted ((owned)creds); + break; + case 1: + debug ("decrypt_partition result is 1"); + break; + case 2: + debug ("decrypt: input was not valid UTF-8"); + break; + case 3: + debug ("decrypt: either a password or keydata string must be supplied"); + break; + case 4: + debug ("decrypt: unable to decrypt partition (possibly invalid password)"); + break; + case 5: + debug ("decrypt: the decrypted partition does not have a LVM volume on it"); + break; + case 6: + debug ("decrypt: unable to locate LUKS partition at %s", device_path); + break; + default: + critical ("decrypt: unhandled error value: %d", result); + break; + } + } + private void create_decrypted_view (string pv) { var label = new Gtk.Label ("%s".printf (pv)); label.use_markup = true; diff --git a/src/Widgets/PartitionBar.vala b/src/Widgets/PartitionBar.vala index 84a6b8aa7..30f77a7d6 100644 --- a/src/Widgets/PartitionBar.vala +++ b/src/Widgets/PartitionBar.vala @@ -27,31 +27,30 @@ public class Installer.PartitionBar : Gtk.EventBox { public new string path; public string? vg; - public Distinst.Partition* info; public Gtk.Label label; public Gtk.Popover menu; public Distinst.FileSystem filesystem; - public PartitionBar (Distinst.Partition* part, string parent_path, + public signal void decrypted (InstallerDaemon.LuksCredentials credential); + + public PartitionBar (InstallerDaemon.Partition part, string parent_path, uint64 sector_size, bool lvm, SetMount set_mount, - UnsetMount unset_mount, MountSetFn mount_set, - DecryptFn decrypt) { - start = part->get_start_sector (); - end = part->get_end_sector (); + UnsetMount unset_mount, MountSetFn mount_set) { + start = part.start_sector; + end = part.end_sector; - var usage = part->sectors_used (sector_size); + var usage = part.sectors_used; if (usage.tag == 1) { used = usage.value; } else { used = end - start; } - path = Utils.string_from_utf8 (part->get_device_path ()); - filesystem = part->get_file_system (); + path = part.device_path; + filesystem = part.filesystem; vg = (Distinst.FileSystem.LVM == filesystem) - ? Utils.string_from_utf8 (part->get_current_lvm_volume_group ()) + ? part.current_lvm_volume_group : null; - info = part; tooltip_text = path; var style_context = get_style_context (); @@ -61,7 +60,8 @@ public class Installer.PartitionBar : Gtk.EventBox { container = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); if (filesystem == Distinst.FileSystem.LUKS) { - menu = new DecryptMenu (path, decrypt); + menu = new DecryptMenu (path); + ((DecryptMenu)menu).decrypted.connect ((creds) => decrypted (creds)); } else { menu = new PartitionMenu (path, parent_path, filesystem, lvm, set_mount, unset_mount, mount_set, this); diff --git a/src/Widgets/PartitionMenu.vala b/src/Widgets/PartitionMenu.vala index b34bf8bf1..0c578813f 100644 --- a/src/Widgets/PartitionMenu.vala +++ b/src/Widgets/PartitionMenu.vala @@ -51,7 +51,7 @@ public class Installer.PartitionMenu : Gtk.Popover { partition_path = path; parent_disk = parent; - string boot_partition = (Distinst.bootloader_detect () == Distinst.PartitionTable.GPT) + string boot_partition = (Daemon.get_default ().bootloader_detect () == Distinst.PartitionTable.GPT) ? "/boot/efi" : "/boot"; @@ -177,7 +177,7 @@ public class Installer.PartitionMenu : Gtk.Popover { custom.visible = visible; if (active == 2) { - if (Distinst.bootloader_detect () == Distinst.PartitionTable.GPT) { + if (Daemon.get_default ().bootloader_detect () == Distinst.PartitionTable.GPT) { type.active = 2; } else { type.active = 0; @@ -287,8 +287,8 @@ public class Installer.PartitionMenu : Gtk.Popover { parent_disk, mount, partition_bar.end - partition_bar.start, - (format_partition.active ? Mount.Flags.FORMAT : 0) - + (is_lvm ? Mount.Flags.LVM : 0), + (format_partition.active ? InstallerDaemon.MountFlags.FORMAT : 0) + + (is_lvm ? InstallerDaemon.MountFlags.LVM : 0), filesystem, this )); @@ -344,7 +344,7 @@ public class Installer.PartitionMenu : Gtk.Popover { case 1: return "/home"; case 2: - if (Distinst.bootloader_detect () == Distinst.PartitionTable.GPT) { + if (Daemon.get_default ().bootloader_detect () == Distinst.PartitionTable.GPT) { return "/boot/efi"; } else { return "/boot"; diff --git a/src/meson.build b/src/meson.build index 533f7fb7c..97e0ad460 100644 --- a/src/meson.build +++ b/src/meson.build @@ -3,6 +3,7 @@ vala_files = [ 'MainWindow.vala', 'Utils.vala', 'Helpers/Inhibitor.vala', + 'Helpers/InstallerDaemon.vala', 'Helpers/KeyboardLayoutHelper.vala', 'Helpers/LocaleHelper.vala', 'Helpers/LogHelper.vala', @@ -27,7 +28,8 @@ vala_files = [ 'Widgets/PartitionBar.vala', 'Widgets/PartitionMenu.vala', 'Widgets/Terminal.vala', - 'Widgets/VariantWidget.vala' + 'Widgets/VariantWidget.vala', + common_files, ] configuration_data = configuration_data() @@ -51,7 +53,22 @@ config_file = configure_file( configuration: configuration_data ) +gui_dependencies = [ + distinst_dep, + gee_dep, + glib_dep, + gnome_keyboard_dep, + gnome_keyboard_ui_dep, + gobject_dep, + granite_dep, + gtk_dep, + handy_dep, + json_glib_dep, + pwquality_dep, + xml2_dep +] + executable(meson.project_name(), vala_files, config_file, asresources, - dependencies : dependencies, + dependencies : gui_dependencies, install: true)