forked from brl/citadel-tools
installer refactored extensively
This commit is contained in:
parent
ffd7a78c3d
commit
c5d5693f62
@ -59,50 +59,3 @@ impl Disk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn rootfs_channel() -> &'static str {
|
|
||||||
match OsRelease::citadel_channel() {
|
|
||||||
Some(channel) => channel,
|
|
||||||
None => "dev",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exec_cmdline<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<()> {
|
|
||||||
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
|
|
||||||
let status = Command::new(cmd_path)
|
|
||||||
.args(args)
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
match status.code() {
|
|
||||||
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
|
|
||||||
None => bail!("command {} failed with no exit code", cmd_path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exec_cmdline_with_output<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<String> {
|
|
||||||
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
|
|
||||||
let res = Command::new(cmd_path)
|
|
||||||
.args(args)
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.output()
|
|
||||||
.context(format!("unable to execute {}", cmd_path))?;
|
|
||||||
|
|
||||||
check_cmd_status(cmd_path, &res.status)?;
|
|
||||||
Ok(String::from_utf8(res.stdout).unwrap().trim().to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_cmd_status(cmd_path: &str, status: &ExitStatus) -> Result<()> {
|
|
||||||
if !status.success() {
|
|
||||||
match status.code() {
|
|
||||||
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
|
|
||||||
None => bail!("command {} failed with no exit code", cmd_path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
@ -1,37 +1,19 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fs::{self,File};
|
use std::fs::{self,File};
|
||||||
use std::io::Write;
|
use std::io::{self,Write};
|
||||||
use std::os::unix::fs as unixfs;
|
use std::os::unix::fs as unixfs;
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use libcitadel::util::{self,mount,exec_cmdline_with_output};
|
use libcitadel::util;
|
||||||
use libcitadel::RealmFS;
|
use libcitadel::RealmFS;
|
||||||
use libcitadel::Result;
|
use libcitadel::Result;
|
||||||
use libcitadel::OsRelease;
|
use libcitadel::OsRelease;
|
||||||
use libcitadel::KeyRing;
|
use libcitadel::KeyRing;
|
||||||
|
use libcitadel::terminal::Base16Scheme;
|
||||||
const BLKDEACTIVATE: &str = "/sbin/blkdeactivate";
|
use libcitadel::UtsName;
|
||||||
const CRYPTSETUP: &str = "/sbin/cryptsetup";
|
|
||||||
const PARTED: &str = "/sbin/parted";
|
|
||||||
const EXTLINUX: &str = "/sbin/extlinux";
|
|
||||||
const PVCREATE: &str = "/sbin/pvcreate";
|
|
||||||
const VGCREATE: &str = "/sbin/vgcreate";
|
|
||||||
const LVCREATE: &str = "/sbin/lvcreate";
|
|
||||||
const VGCHANGE: &str = "/sbin/vgchange";
|
|
||||||
const MKFS_VFAT: &str = "/sbin/mkfs.vfat";
|
|
||||||
const MKFS_BTRFS: &str = "/bin/mkfs.btrfs";
|
|
||||||
const LSBLK: &str = "/bin/lsblk";
|
|
||||||
const BTRFS: &str = "/bin/btrfs";
|
|
||||||
const MOUNT: &str = "/bin/mount";
|
|
||||||
const UMOUNT: &str = "/bin/umount";
|
|
||||||
const CHOWN: &str = "/bin/chown";
|
|
||||||
const CP: &str = "/bin/cp";
|
|
||||||
const TAR: &str = "/bin/tar";
|
|
||||||
const XZ: &str = "/bin/xz";
|
|
||||||
const DD: &str = "/bin/dd";
|
|
||||||
const CITADEL_IMAGE: &str = "/usr/bin/citadel-image";
|
|
||||||
|
|
||||||
const LUKS_UUID: &str = "683a17fc-4457-42cc-a946-cde67195a101";
|
const LUKS_UUID: &str = "683a17fc-4457-42cc-a946-cde67195a101";
|
||||||
|
|
||||||
@ -44,18 +26,90 @@ const DEFAULT_ARTIFACT_DIRECTORY: &str = "/run/citadel/images";
|
|||||||
|
|
||||||
const KERNEL_CMDLINE: &str = "add_efi_memmap intel_iommu=off cryptomgr.notests rcupdate.rcu_expedited=1 rcu_nocbs=0-64 tsc=reliable no_timer_check noreplace-smp i915.fastboot=1 quiet splash";
|
const KERNEL_CMDLINE: &str = "add_efi_memmap intel_iommu=off cryptomgr.notests rcupdate.rcu_expedited=1 rcu_nocbs=0-64 tsc=reliable no_timer_check noreplace-smp i915.fastboot=1 quiet splash";
|
||||||
|
|
||||||
const MAIN_REALM_CONFIG: &str =
|
const GLOBAL_REALM_CONFIG: &str = "\
|
||||||
r###"\
|
realmfs = 'main'
|
||||||
realmfs = "main"
|
realm-depends = ['apt-cacher']
|
||||||
realmfs-write = true
|
";
|
||||||
"###;
|
|
||||||
|
|
||||||
const LIVE_REALM_CONFIG: &str =
|
const LIVE_REALM_CONFIG: &str = "\
|
||||||
r###"\
|
realmfs = 'base'
|
||||||
realmfs = "base"
|
overlay = 'tmpfs'
|
||||||
realmfs-write = false
|
realm-depends = ['apt-cacher']
|
||||||
"###;
|
";
|
||||||
|
|
||||||
|
const APT_CACHER_CONFIG: &str = "\
|
||||||
|
use-shared-dir = false
|
||||||
|
use-sound = false
|
||||||
|
use-x11 = false
|
||||||
|
use-wayland = false
|
||||||
|
system-realm = true
|
||||||
|
reserved-ip = 213
|
||||||
|
extra-bindmounts-ro = [ '/usr/share/apt-cacher-ng' ]
|
||||||
|
";
|
||||||
|
|
||||||
|
const MAIN_CONFIG: &str = "\
|
||||||
|
terminal-scheme = '$SCHEME'
|
||||||
|
";
|
||||||
|
|
||||||
|
const MAIN_TERMINAL_SCHEME: &str = "embers";
|
||||||
|
|
||||||
|
const PARTITION_COMMANDS: &[&str] = &[
|
||||||
|
"/sbin/blkdeactivate $TARGET",
|
||||||
|
"/sbin/parted -s $TARGET mklabel gpt",
|
||||||
|
"/sbin/parted -s $TARGET mkpart boot fat32 1MiB 513MiB",
|
||||||
|
"/sbin/parted -s $TARGET set 1 boot on",
|
||||||
|
"/sbin/parted -s $TARGET mkpart data ext4 513MiB 100%",
|
||||||
|
"/sbin/parted -s $TARGET set 2 lvm on",
|
||||||
|
];
|
||||||
|
|
||||||
|
const LUKS_COMMANDS: &[&str] = &[
|
||||||
|
"/sbin/cryptsetup -q --uuid=$LUKS_UUID luksFormat $LUKS_PARTITION $LUKS_PASSFILE",
|
||||||
|
"/sbin/cryptsetup open --type luks --key-file $LUKS_PASSFILE $LUKS_PARTITION luks-install",
|
||||||
|
];
|
||||||
|
|
||||||
|
const LVM_COMMANDS: &[&str] = &[
|
||||||
|
"/sbin/pvcreate -ff --yes /dev/mapper/luks-install",
|
||||||
|
"/sbin/vgcreate --yes citadel /dev/mapper/luks-install",
|
||||||
|
"/sbin/lvcreate --yes --size 2g --name rootfsA citadel",
|
||||||
|
"/sbin/lvcreate --yes --size 2g --name rootfsB citadel",
|
||||||
|
"/sbin/lvcreate --yes --extents 100%VG --name storage citadel",
|
||||||
|
];
|
||||||
|
|
||||||
|
const CREATE_STORAGE_COMMANDS: &[&str] = &[
|
||||||
|
"/bin/mkfs.btrfs /dev/mapper/citadel-storage",
|
||||||
|
"/bin/mount /dev/mapper/citadel-storage $INSTALL_MOUNT",
|
||||||
|
];
|
||||||
|
|
||||||
|
const FINISH_COMMANDS: &[&str] = &[
|
||||||
|
"/bin/lsblk -o NAME,SIZE,TYPE,FSTYPE $TARGET",
|
||||||
|
"/sbin/vgchange -an citadel",
|
||||||
|
"/sbin/cryptsetup luksClose luks-install",
|
||||||
|
];
|
||||||
|
|
||||||
|
const LOADER_CONF: &str = "\
|
||||||
|
default citadel
|
||||||
|
timeout 5
|
||||||
|
";
|
||||||
|
|
||||||
|
const BOOT_CONF: &str = "\
|
||||||
|
title Subgraph OS (Citadel)
|
||||||
|
linux /bzImage
|
||||||
|
options root=/dev/mapper/rootfs $KERNEL_CMDLINE
|
||||||
|
";
|
||||||
|
|
||||||
|
const SYSLINUX_CONF: &str = "\
|
||||||
|
UI menu.c32
|
||||||
|
PROMPT 0
|
||||||
|
|
||||||
|
MENU TITLE Boot Subgraph OS (Citadel)
|
||||||
|
TIMEOUT 50
|
||||||
|
DEFAULT subgraph
|
||||||
|
|
||||||
|
LABEL subgraph
|
||||||
|
MENU LABEL Subgraph OS
|
||||||
|
LINUX ../bzImage
|
||||||
|
APPEND root=/dev/mapper/rootfs $KERNEL_CMDLINE
|
||||||
|
";
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum InstallType {
|
enum InstallType {
|
||||||
@ -104,6 +158,10 @@ impl Installer {
|
|||||||
self.target_device.as_ref().expect("No target device")
|
self.target_device.as_ref().expect("No target device")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn target_str(&self) -> &str {
|
||||||
|
self.target().to_str().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn passphrase(&self) -> &str {
|
fn passphrase(&self) -> &str {
|
||||||
self.passphrase.as_ref().expect("No passphrase")
|
self.passphrase.as_ref().expect("No passphrase")
|
||||||
}
|
}
|
||||||
@ -117,11 +175,6 @@ impl Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self) -> Result<()> {
|
pub fn verify(&self) -> Result<()> {
|
||||||
let tools = vec![
|
|
||||||
BLKDEACTIVATE,CRYPTSETUP,PARTED,EXTLINUX,PVCREATE,VGCREATE,LVCREATE,VGCHANGE,
|
|
||||||
MKFS_VFAT,MKFS_BTRFS,LSBLK,BTRFS,MOUNT,UMOUNT,CHOWN,TAR,XZ,CITADEL_IMAGE,
|
|
||||||
];
|
|
||||||
|
|
||||||
let kernel_img = self.kernel_imagename();
|
let kernel_img = self.kernel_imagename();
|
||||||
let artifacts = vec![
|
let artifacts = vec![
|
||||||
"bootx64.efi", "bzImage",
|
"bootx64.efi", "bzImage",
|
||||||
@ -132,12 +185,6 @@ impl Installer {
|
|||||||
bail!("Target device {} does not exist", self.target().display());
|
bail!("Target device {} does not exist", self.target().display());
|
||||||
}
|
}
|
||||||
|
|
||||||
for tool in tools {
|
|
||||||
if !Path::new(tool).exists() {
|
|
||||||
bail!("Required installer utility program does not exist: {}", tool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for a in artifacts {
|
for a in artifacts {
|
||||||
if !self.artifact_path(a).exists() {
|
if !self.artifact_path(a).exists() {
|
||||||
bail!("Required install artifact {} does not exist in {}", a, self.artifact_directory);
|
bail!("Required install artifact {} does not exist in {}", a, self.artifact_directory);
|
||||||
@ -167,13 +214,15 @@ impl Installer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn run_live_setup(&self) -> Result<()> {
|
pub fn run_live_setup(&self) -> Result<()> {
|
||||||
self.cmd(MOUNT, "-t tmpfs var-tmpfs /sysroot/var")?;
|
self.cmd_list(&[
|
||||||
self.cmd(MOUNT, "-t tmpfs home-tmpfs /sysroot/home")?;
|
"/bin/mount -t tmpfs var-tmpfs /sysroot/var",
|
||||||
self.cmd(MOUNT, "-t tmpfs storage-tmpfs /sysroot/storage")?;
|
"/bin/mount -t tmpfs home-tmpfs /sysroot/home",
|
||||||
|
"/bin/mount -t tmpfs storage-tmpfs /sysroot/storage",
|
||||||
|
], &[])?;
|
||||||
|
|
||||||
fs::create_dir_all("/sysroot/storage/realms")?;
|
fs::create_dir_all("/sysroot/storage/realms")?;
|
||||||
self.cmd(MOUNT, "--bind /sysroot/storage/realms /sysroot/realms")?;
|
self.cmd("/bin/mount --bind /sysroot/storage/realms /sysroot/realms")?;
|
||||||
|
|
||||||
let cmdline = fs::read_to_string("/proc/cmdline")?;
|
let cmdline = fs::read_to_string("/proc/cmdline")?;
|
||||||
if cmdline.contains("citadel.live") {
|
if cmdline.contains("citadel.live") {
|
||||||
@ -183,7 +232,7 @@ impl Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn setup_live_realm(&self) -> Result<()> {
|
fn setup_live_realm(&self) -> Result<()> {
|
||||||
self.cmd(CITADEL_IMAGE, format!("decompress /run/citadel/images/base-realmfs.img"))?;
|
|
||||||
let realmfs_dir = self.storage().join("realms/realmfs-images");
|
let realmfs_dir = self.storage().join("realms/realmfs-images");
|
||||||
let base_realmfs = realmfs_dir.join("base-realmfs.img");
|
let base_realmfs = realmfs_dir.join("base-realmfs.img");
|
||||||
|
|
||||||
@ -192,48 +241,20 @@ impl Installer {
|
|||||||
|
|
||||||
self.info(format!("creating symlink {} -> {}", base_realmfs.display(), "/run/citadel/images/base-realmfs.img"))?;
|
self.info(format!("creating symlink {} -> {}", base_realmfs.display(), "/run/citadel/images/base-realmfs.img"))?;
|
||||||
unixfs::symlink("/run/citadel/images/base-realmfs.img", &base_realmfs)?;
|
unixfs::symlink("/run/citadel/images/base-realmfs.img", &base_realmfs)?;
|
||||||
self.mount_realmfs()?;
|
|
||||||
|
let realmfs = RealmFS::load_from_path("/run/citadel/images/base-realmfs.img")?;
|
||||||
|
realmfs.activate()?;
|
||||||
|
|
||||||
self.setup_storage()?;
|
self.setup_storage()?;
|
||||||
|
|
||||||
/*
|
|
||||||
self.setup_main_realm()?;
|
|
||||||
fs::write(self.storage().join("realms/realm-main/config"), "realmfs = \"base\"")?;
|
|
||||||
let rootfs = self.storage().join("realms/realm-main/rootfs");
|
|
||||||
fs::remove_file(&rootfs)?;
|
|
||||||
unixfs::symlink("/run/citadel/images/base-realmfs.mountpoint", &rootfs)?;
|
|
||||||
|
|
||||||
self.info("Creating /Shared realms directory")?;
|
|
||||||
fs::create_dir_all(self.storage().join("realms/Shared"))?;
|
|
||||||
self.cmd(CHOWN, format!("1000:1000 {}/realms/Shared", self.storage().display()))?;
|
|
||||||
*/
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mount_realmfs(&self) -> Result<()> {
|
|
||||||
self.info("Creating loop device for /run/citadel/images/base-realmfs.img")?;
|
|
||||||
let args = format!("--offset 4096 -f --show /run/citadel/images/base-realmfs.img");
|
|
||||||
let loopdev = exec_cmdline_with_output("/sbin/losetup", args)?;
|
|
||||||
self.info("Mounting image at /run/citadel/images/base-realmfs.mountpoint")?;
|
|
||||||
fs::create_dir_all("/run/citadel/images/base-realmfs.mountpoint")?;
|
|
||||||
mount(&loopdev, "/run/citadel/images/base-realmfs.mountpoint", Some("-oro"))?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partition_disk(&self) -> Result<()> {
|
fn partition_disk(&self) -> Result<()> {
|
||||||
self.header("Partitioning target disk")?;
|
self.header("Partitioning target disk")?;
|
||||||
self.cmd(BLKDEACTIVATE, self.target().display().to_string())?;
|
self.cmd_list(PARTITION_COMMANDS, &[
|
||||||
self.parted("mklabel gpt")?;
|
("$TARGET", self.target_str())
|
||||||
self.parted("mkpart boot fat32 1MiB 513MiB")?;
|
])
|
||||||
self.parted("set 1 boot on")?;
|
|
||||||
self.parted("mkpart data ext4 513MiB 100%")?;
|
|
||||||
self.parted("set 2 lvm on")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parted(&self, cmdline: &str) -> Result<()> {
|
|
||||||
let args = format!("-s {} {}", self.target().display(), cmdline);
|
|
||||||
self.cmd(PARTED, args)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_luks(&self) -> Result<()> {
|
fn setup_luks(&self) -> Result<()> {
|
||||||
@ -242,46 +263,36 @@ impl Installer {
|
|||||||
fs::write(LUKS_PASSPHRASE_FILE, self.passphrase().as_bytes())?;
|
fs::write(LUKS_PASSPHRASE_FILE, self.passphrase().as_bytes())?;
|
||||||
let luks_partition = self.target_partition(2);
|
let luks_partition = self.target_partition(2);
|
||||||
|
|
||||||
let args = format!(
|
self.cmd_list(LUKS_COMMANDS, &[
|
||||||
"-q --uuid={} luksFormat {} {}",
|
("$LUKS_UUID", LUKS_UUID),
|
||||||
LUKS_UUID, luks_partition, LUKS_PASSPHRASE_FILE
|
("$LUKS_PARTITION", &luks_partition),
|
||||||
);
|
("$LUKS_PASSFILE", LUKS_PASSPHRASE_FILE),
|
||||||
self.cmd(CRYPTSETUP, args)?;
|
])?;
|
||||||
|
|
||||||
let args = format!(
|
|
||||||
"open --type luks --key-file {} {} luks-install",
|
|
||||||
LUKS_PASSPHRASE_FILE, luks_partition
|
|
||||||
);
|
|
||||||
self.cmd(CRYPTSETUP, args)?;
|
|
||||||
fs::remove_file(LUKS_PASSPHRASE_FILE)?;
|
fs::remove_file(LUKS_PASSPHRASE_FILE)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_lvm(&self) -> Result<()> {
|
fn setup_lvm(&self) -> Result<()> {
|
||||||
self.header("Setting up LVM volumes")?;
|
self.header("Setting up LVM volumes")?;
|
||||||
self.cmd(PVCREATE, "-ff --yes /dev/mapper/luks-install")?;
|
self.cmd_list(LVM_COMMANDS, &[])
|
||||||
self.cmd(VGCREATE, "--yes citadel /dev/mapper/luks-install")?;
|
|
||||||
|
|
||||||
self.cmd(LVCREATE, "--yes --size 2g --name rootfsA citadel")?;
|
|
||||||
self.cmd(LVCREATE, "--yes --size 2g --name rootfsB citadel")?;
|
|
||||||
self.cmd(LVCREATE, "--yes --extents 100%VG --name storage citadel")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_boot(&self) -> Result<()> {
|
fn setup_boot(&self) -> Result<()> {
|
||||||
self.header("Setting up /boot partition")?;
|
self.header("Setting up /boot partition")?;
|
||||||
let boot_partition = self.target_partition(1);
|
let boot_partition = self.target_partition(1);
|
||||||
self.cmd(MKFS_VFAT, format!("-F 32 {}", boot_partition))?;
|
self.cmd(format!("/sbin/mkfs.vfat -F 32 {}", boot_partition))?;
|
||||||
|
|
||||||
self.cmd(MOUNT, format!("{} {}", boot_partition, INSTALL_MOUNT))?;
|
self.cmd(format!("/bin/mount {} {}", boot_partition, INSTALL_MOUNT))?;
|
||||||
|
|
||||||
fs::create_dir_all(format!("{}/loader/entries", INSTALL_MOUNT))?;
|
fs::create_dir_all(format!("{}/loader/entries", INSTALL_MOUNT))?;
|
||||||
|
|
||||||
self.info("Writing /boot/loader/loader.conf")?;
|
self.info("Writing /boot/loader/loader.conf")?;
|
||||||
fs::write(format!("{}/loader/loader.conf", INSTALL_MOUNT), self.loader_conf())?;
|
fs::write(format!("{}/loader/loader.conf", INSTALL_MOUNT), LOADER_CONF)?;
|
||||||
|
|
||||||
self.info("Writing /boot/entries/citadel.conf")?;
|
self.info("Writing /boot/entries/citadel.conf")?;
|
||||||
fs::write(format!("{}/loader/entries/citadel.conf", INSTALL_MOUNT), self.boot_conf())?;
|
fs::write(format!("{}/loader/entries/citadel.conf", INSTALL_MOUNT),
|
||||||
|
BOOT_CONF.replace("$KERNEL_CMDLINE", KERNEL_CMDLINE))?;
|
||||||
|
|
||||||
self.copy_artifact("bzImage", INSTALL_MOUNT)?;
|
self.copy_artifact("bzImage", INSTALL_MOUNT)?;
|
||||||
self.copy_artifact("bootx64.efi", format!("{}/EFI/BOOT", INSTALL_MOUNT))?;
|
self.copy_artifact("bootx64.efi", format!("{}/EFI/BOOT", INSTALL_MOUNT))?;
|
||||||
@ -290,7 +301,7 @@ impl Installer {
|
|||||||
self.setup_syslinux()?;
|
self.setup_syslinux()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cmd(UMOUNT, INSTALL_MOUNT)?;
|
self.cmd(format!("/bin/umount {}", INSTALL_MOUNT))?;
|
||||||
|
|
||||||
if self.install_syslinux {
|
if self.install_syslinux {
|
||||||
self.setup_syslinux_post_umount()?;
|
self.setup_syslinux_post_umount()?;
|
||||||
@ -299,21 +310,6 @@ impl Installer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loader_conf(&self) -> Vec<u8> {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
writeln!(&mut v, "default citadel").unwrap();
|
|
||||||
writeln!(&mut v, "timeout 5").unwrap();
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
fn boot_conf(&self) -> Vec<u8> {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
writeln!(&mut v, "title Subgraph OS (Citadel)").unwrap();
|
|
||||||
writeln!(&mut v, "linux /bzImage").unwrap();
|
|
||||||
writeln!(&mut v, "options root=/dev/mapper/rootfs {}", KERNEL_CMDLINE).unwrap();
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_syslinux(&self) -> Result<()> {
|
fn setup_syslinux(&self) -> Result<()> {
|
||||||
self.header("Installing syslinux")?;
|
self.header("Installing syslinux")?;
|
||||||
let syslinux_src = self.artifact_path("syslinux");
|
let syslinux_src = self.artifact_path("syslinux");
|
||||||
@ -328,8 +324,9 @@ impl Installer {
|
|||||||
fs::copy(entry.path(), dst.join(entry.file_name()))?;
|
fs::copy(entry.path(), dst.join(entry.file_name()))?;
|
||||||
}
|
}
|
||||||
self.info("Writing syslinux.cfg")?;
|
self.info("Writing syslinux.cfg")?;
|
||||||
fs::write(dst.join("syslinux.cfg"), self.syslinux_conf())?;
|
fs::write(dst.join("syslinux.cfg"),
|
||||||
self.cmd(EXTLINUX, format!("--install {}", dst.display()))?;
|
SYSLINUX_CONF.replace("$KERNEL_CMDLINE", KERNEL_CMDLINE))?;
|
||||||
|
self.cmd(format!("/sbin/extlinux --install {}", dst.display()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,34 +335,19 @@ impl Installer {
|
|||||||
if !mbrbin.exists() {
|
if !mbrbin.exists() {
|
||||||
bail!("Could not find MBR image: {}", mbrbin.display());
|
bail!("Could not find MBR image: {}", mbrbin.display());
|
||||||
}
|
}
|
||||||
let args = format!("bs=440 count=1 conv=notrunc if={} of={}", mbrbin.display(), self.target().display());
|
self.cmd(format!("/bin/dd bs=440 count=1 conv=notrunc if={} of={}", mbrbin.display(), self.target().display()))?;
|
||||||
self.cmd(DD, args)?;
|
self.cmd(format!("/sbin/parted -s {} set 1 legacy_boot on", self.target_str()))
|
||||||
self.parted("set 1 legacy_boot on")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn syslinux_conf(&self) -> Vec<u8> {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
writeln!(&mut v, "UI menu.c32").unwrap();
|
|
||||||
writeln!(&mut v, "PROMPT 0").unwrap();
|
|
||||||
writeln!(&mut v, "").unwrap();
|
|
||||||
writeln!(&mut v, "MENU TITLE Boot Subgraph OS (Citadel)").unwrap();
|
|
||||||
writeln!(&mut v, "TIMEOUT 50").unwrap();
|
|
||||||
writeln!(&mut v, "DEFAULT subgraph").unwrap();
|
|
||||||
writeln!(&mut v, "").unwrap();
|
|
||||||
writeln!(&mut v, "LABEL subgraph").unwrap();
|
|
||||||
writeln!(&mut v, " MENU LABEL Subgraph OS").unwrap();
|
|
||||||
writeln!(&mut v, " LINUX ../bzImage").unwrap();
|
|
||||||
writeln!(&mut v, " APPEND root=/dev/mapper/rootfs {}", KERNEL_CMDLINE).unwrap();
|
|
||||||
v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_storage(&self) -> Result<()> {
|
fn create_storage(&self) -> Result<()> {
|
||||||
self.header("Setting up /storage partition")?;
|
self.header("Setting up /storage partition")?;
|
||||||
self.cmd(MKFS_BTRFS, "/dev/mapper/citadel-storage")?;
|
|
||||||
self.cmd(MOUNT, format!("/dev/mapper/citadel-storage {}", INSTALL_MOUNT))?;
|
self.cmd_list(CREATE_STORAGE_COMMANDS,
|
||||||
|
&[("$INSTALL_MOUNT", INSTALL_MOUNT)])?;
|
||||||
|
|
||||||
self.setup_storage()?;
|
self.setup_storage()?;
|
||||||
self.cmd(UMOUNT, INSTALL_MOUNT)?;
|
self.cmd(format!("/bin/umount {}", INSTALL_MOUNT))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,17 +358,24 @@ impl Installer {
|
|||||||
self.setup_base_realmfs()?;
|
self.setup_base_realmfs()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.setup_realm_skel()?;
|
||||||
self.setup_main_realm()?;
|
self.setup_main_realm()?;
|
||||||
|
self.setup_apt_cacher_realm()?;
|
||||||
|
|
||||||
|
self.info("Creating global realm config file")?;
|
||||||
|
fs::write(self.storage().join("realms/config"), self.global_realm_config())?;
|
||||||
|
|
||||||
self.info("Creating /Shared realms directory")?;
|
self.info("Creating /Shared realms directory")?;
|
||||||
fs::create_dir_all(self.storage().join("realms/Shared"))?;
|
|
||||||
self.cmd(CHOWN, format!("1000:1000 {}/realms/Shared", self.storage().display()))?;
|
let shared = self.storage().join("realms/Shared");
|
||||||
|
fs::create_dir_all(&shared)?;
|
||||||
|
util::chown_user(&shared)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_keyring(&self) -> Result<()> {
|
fn create_keyring(&self) -> Result<()> {
|
||||||
self.header("Creating initial keyring")?;
|
self.info("Creating initial keyring")?;
|
||||||
let keyring = KeyRing::create_new();
|
let keyring = KeyRing::create_new();
|
||||||
keyring.write(self.storage().join("keyring"), self.passphrase.as_ref().unwrap())
|
keyring.write(self.storage().join("keyring"), self.passphrase.as_ref().unwrap())
|
||||||
}
|
}
|
||||||
@ -395,13 +384,15 @@ impl Installer {
|
|||||||
let realmfs_dir = self.storage().join("realms/realmfs-images");
|
let realmfs_dir = self.storage().join("realms/realmfs-images");
|
||||||
fs::create_dir_all(&realmfs_dir)?;
|
fs::create_dir_all(&realmfs_dir)?;
|
||||||
self.sparse_copy_artifact("base-realmfs.img", &realmfs_dir)?;
|
self.sparse_copy_artifact("base-realmfs.img", &realmfs_dir)?;
|
||||||
self.cmd(CITADEL_IMAGE, format!("decompress {}/base-realmfs.img", realmfs_dir.display()))?;
|
self.cmd(format!("/usr/bin/citadel-image decompress {}/base-realmfs.img", realmfs_dir.display()))?;
|
||||||
|
|
||||||
self.info("Creating main-realmfs as fork of base-realmfs")?;
|
Ok(())
|
||||||
let base_path = realmfs_dir.join("base-realmfs.img");
|
}
|
||||||
let base_image = RealmFS::load_from_path(base_path, "base")?;
|
|
||||||
base_image.fork("main")?;
|
fn setup_realm_skel(&self) -> Result<()> {
|
||||||
fs::write(self.storage().join("realms/config"), "realmfs=\"main\"\n")?;
|
let realm_skel = self.storage().join("realms/skel");
|
||||||
|
fs::create_dir_all(&realm_skel)?;
|
||||||
|
util::copy_tree_with_chown(&self.skel(), &realm_skel, (1000,1000))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,26 +404,43 @@ impl Installer {
|
|||||||
self.info("Creating home directory /realms/realm-main/home")?;
|
self.info("Creating home directory /realms/realm-main/home")?;
|
||||||
let home = realm.join("home");
|
let home = realm.join("home");
|
||||||
fs::create_dir_all(&home)?;
|
fs::create_dir_all(&home)?;
|
||||||
|
util::chown_user(&home)?;
|
||||||
|
|
||||||
self.info("Copying .bashrc and .profile into home diectory")?;
|
self.info("Copying /realms/skel into home diectory")?;
|
||||||
fs::copy(self.skel().join("bashrc"), home.join(".bashrc"))?;
|
util::copy_tree(&self.storage().join("realms/skel"), &home)?;
|
||||||
fs::copy(self.skel().join("profile"), home.join(".profile"))?;
|
|
||||||
|
|
||||||
self.cmd(CHOWN, format!("-R 1000:1000 {}", home.display()))?;
|
if let Some(scheme) = Base16Scheme::by_name(MAIN_TERMINAL_SCHEME) {
|
||||||
|
scheme.write_realm_files(&home)?;
|
||||||
self.info("Creating main realm config file")?;
|
fs::write(realm.join("config"), MAIN_CONFIG.replace("$SCHEME", MAIN_TERMINAL_SCHEME))?;
|
||||||
fs::write(realm.join("config"), self.main_realm_config())?;
|
}
|
||||||
|
util::chown_tree(&home, (1000,1000), false)?;
|
||||||
/*
|
|
||||||
self.info("Creating rootfs symlink")?;
|
|
||||||
unixfs::symlink(
|
|
||||||
format!("/run/citadel/images/{}-realmfs.mountpoint", self.main_realmfs()),
|
|
||||||
format!("{}/rootfs", realm.display()))?;
|
|
||||||
*/
|
|
||||||
|
|
||||||
self.info("Creating default.realm symlink")?;
|
self.info("Creating default.realm symlink")?;
|
||||||
unixfs::symlink("realm-main", self.storage().join("realms/default.realm"))?;
|
unixfs::symlink("/realms/realm-main", self.storage().join("realms/default.realm"))?;
|
||||||
|
|
||||||
|
fs::File::create(realm.join(".realmlock"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_apt_cacher_realm(&self) -> Result<()> {
|
||||||
|
self.header("Creating apt-cacher realm")?;
|
||||||
|
let realm_base = self.storage().join("realms/realm-apt-cacher");
|
||||||
|
|
||||||
|
self.info("Creating home directory /realms/realm-apt-cacher/home")?;
|
||||||
|
let home = realm_base.join("home");
|
||||||
|
fs::create_dir_all(&home)?;
|
||||||
|
util::chown_user(&home)?;
|
||||||
|
let path = home.join("apt-cacher-ng");
|
||||||
|
fs::create_dir_all(&path)?;
|
||||||
|
util::chown_user(&path)?;
|
||||||
|
|
||||||
|
self.info("Copying /realms/skel into home diectory")?;
|
||||||
|
util::copy_tree(&self.storage().join("realms/skel"), &home)?;
|
||||||
|
|
||||||
|
self.info("Creating apt-cacher config file")?;
|
||||||
|
fs::write(realm_base.join("config"), APT_CACHER_CONFIG)?;
|
||||||
|
fs::File::create(realm_base.join(".realmlock"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,34 +463,24 @@ impl Installer {
|
|||||||
fn install_rootfs_partitions(&self) -> Result<()> {
|
fn install_rootfs_partitions(&self) -> Result<()> {
|
||||||
self.header("Installing rootfs partitions")?;
|
self.header("Installing rootfs partitions")?;
|
||||||
let rootfs = self.artifact_path("citadel-rootfs.img");
|
let rootfs = self.artifact_path("citadel-rootfs.img");
|
||||||
self.cmd(CITADEL_IMAGE, format!("install-rootfs --skip-sha {}", rootfs.display()))?;
|
self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha {}", rootfs.display()))?;
|
||||||
self.cmd(CITADEL_IMAGE, format!("install-rootfs --skip-sha --no-prefer {}", rootfs.display()))?;
|
self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha --no-prefer {}", rootfs.display()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_install(&self) -> Result<()> {
|
fn finish_install(&self) -> Result<()> {
|
||||||
self.cmd(LSBLK, format!("-o NAME,SIZE,TYPE,FSTYPE {}", self.target().display()))?;
|
self.cmd_list(FINISH_COMMANDS, &[
|
||||||
self.cmd(VGCHANGE, "-an citadel")?;
|
("$TARGET", self.target_str())
|
||||||
self.cmd(CRYPTSETUP, "luksClose luks-install")?;
|
])
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main_realm_config(&self) -> &str {
|
fn global_realm_config(&self) -> &str {
|
||||||
match self._type {
|
match self._type {
|
||||||
InstallType::Install => MAIN_REALM_CONFIG,
|
InstallType::Install => GLOBAL_REALM_CONFIG,
|
||||||
InstallType::LiveSetup => LIVE_REALM_CONFIG,
|
InstallType::LiveSetup => LIVE_REALM_CONFIG,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
fn main_realmfs(&self) -> &str {
|
|
||||||
match self._type {
|
|
||||||
InstallType::Install => "main",
|
|
||||||
InstallType::LiveSetup => "base",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fn skel(&self) -> &Path{
|
fn skel(&self) -> &Path{
|
||||||
match self._type {
|
match self._type {
|
||||||
InstallType::Install => Path::new("/etc/skel"),
|
InstallType::Install => Path::new("/etc/skel"),
|
||||||
@ -491,7 +489,7 @@ impl Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn kernel_imagename(&self) -> String {
|
fn kernel_imagename(&self) -> String {
|
||||||
let utsname = util::uname();
|
let utsname = UtsName::uname();
|
||||||
let v = utsname.release().split("-").collect::<Vec<_>>();
|
let v = utsname.release().split("-").collect::<Vec<_>>();
|
||||||
format!("citadel-kernel-{}.img", v[0])
|
format!("citadel-kernel-{}.img", v[0])
|
||||||
}
|
}
|
||||||
@ -521,14 +519,13 @@ impl Installer {
|
|||||||
}
|
}
|
||||||
let dst = target.join(filename);
|
let dst = target.join(filename);
|
||||||
if sparse {
|
if sparse {
|
||||||
self.cmd(CP, format!("--sparse=always {} {}", src.display(), dst.display()))?;
|
self.cmd(format!("/bin/cp --sparse=always {} {}", src.display(), dst.display()))?;
|
||||||
} else {
|
} else {
|
||||||
fs::copy(src, dst)?;
|
fs::copy(src, dst)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn header<S: AsRef<str>>(&self, s: S) -> Result<()> {
|
fn header<S: AsRef<str>>(&self, s: S) -> Result<()> {
|
||||||
self.output(format!("\n[+] {}\n", s.as_ref()))
|
self.output(format!("\n[+] {}\n", s.as_ref()))
|
||||||
}
|
}
|
||||||
@ -539,25 +536,43 @@ impl Installer {
|
|||||||
|
|
||||||
fn output<S: AsRef<str>>(&self, s: S) -> Result<()> {
|
fn output<S: AsRef<str>>(&self, s: S) -> Result<()> {
|
||||||
println!("{}", s.as_ref());
|
println!("{}", s.as_ref());
|
||||||
|
io::stdout().flush()?;
|
||||||
|
|
||||||
if let Some(ref file) = self.logfile {
|
if let Some(ref file) = self.logfile {
|
||||||
writeln!(file.borrow_mut(), "{}", s.as_ref())?;
|
writeln!(file.borrow_mut(), "{}", s.as_ref())?;
|
||||||
|
file.borrow_mut().flush()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmd<S: AsRef<str>>(&self, cmd_path: &str, args: S) -> Result<()> {
|
fn cmd_list<I: IntoIterator<Item=S>, S: AsRef<str>>(&self, cmd_lines: I, subs: &[(&str,&str)]) -> Result<()> {
|
||||||
self.output(format!(" # {} {}", cmd_path, args.as_ref()))?;
|
for line in cmd_lines {
|
||||||
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
|
let line = line.as_ref();
|
||||||
let result = Command::new(cmd_path)
|
let line = subs.iter().fold(line.to_string(), |acc, (from,to)| acc.replace(from,to));
|
||||||
.args(args)
|
let args: Vec<&str> = line.split_whitespace().collect::<Vec<_>>();
|
||||||
.output()?;
|
self.run_cmd(args, false)?;
|
||||||
|
|
||||||
if !result.status.success() {
|
|
||||||
match result.status.code() {
|
|
||||||
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
|
|
||||||
None => bail!("command {} failed with no exit code", cmd_path),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmd<S: AsRef<str>>(&self, args: S) -> Result<()> {
|
||||||
|
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
|
||||||
|
self.run_cmd(args, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_cmd(&self, args: Vec<&str>, as_user: bool) -> Result<()> {
|
||||||
|
self.output(format!(" # {}", args.join(" ")))?;
|
||||||
|
|
||||||
|
let mut command = Command::new(args[0]);
|
||||||
|
|
||||||
|
if as_user {
|
||||||
|
command.uid(1000);
|
||||||
|
command.gid(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
command.args(&args[1..]);
|
||||||
|
|
||||||
|
let result = command.output()?;
|
||||||
|
|
||||||
for line in String::from_utf8_lossy(&result.stdout).lines() {
|
for line in String::from_utf8_lossy(&result.stdout).lines() {
|
||||||
self.output(format!(" {}", line))?;
|
self.output(format!(" {}", line))?;
|
||||||
@ -566,6 +581,13 @@ impl Installer {
|
|||||||
for line in String::from_utf8_lossy(&result.stderr).lines() {
|
for line in String::from_utf8_lossy(&result.stderr).lines() {
|
||||||
self.output(format!("! {}", line))?;
|
self.output(format!("! {}", line))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !result.status.success() {
|
||||||
|
match result.status.code() {
|
||||||
|
Some(code) => bail!("command {} failed with exit code: {}", args[0], code),
|
||||||
|
None => bail!("command {} failed with no exit code", args[0]),
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user