Skip to content

Commit

Permalink
wip: continue liveusb command implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
cilki committed May 25, 2024
1 parent 2ce2374 commit e1cf2e3
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 22 deletions.
26 changes: 20 additions & 6 deletions goldboot-image/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ pub enum ImageArch {
S390x,
}

impl ImageArch {
pub fn as_github_string(&self) -> String {
match self {
ImageArch::Amd64 => "x86_64",
ImageArch::Arm64 => todo!(),
ImageArch::I386 => todo!(),
ImageArch::Mips => todo!(),
ImageArch::Mips64 => todo!(),
ImageArch::S390x => todo!(),
}
.to_string()
}
}

impl Default for ImageArch {
fn default() -> Self {
match std::env::consts::ARCH {
Expand Down Expand Up @@ -387,11 +401,11 @@ impl ImageHandle {
let path = path.as_ref();
let mut file = File::open(path)?;

debug!("Opening image from: {}", path.display());
debug!(path = ?path, "Opening image");

// Read primary header (always plaintext)
let primary_header: PrimaryHeader = file.read_be()?;
trace!("Read: {:?}", &primary_header);
debug!(primary_header = ?primary_header, "Primary header");

// Get image ID
let id = if let Some(stem) = path.file_stem() {
Expand Down Expand Up @@ -753,9 +767,9 @@ impl ImageHandle {
let mut cluster: Cluster = cluster_table.read_be()?;

trace!(
"Read cluster of size {} from offset {}",
cluster.size,
entry.cluster_offset
cluster_size = cluster.size,
cluster_offset = entry.cluster_offset,
"Read cluster",
);

// Reverse encryption
Expand All @@ -777,7 +791,7 @@ impl ImageHandle {

// Write the cluster to the block
dest.seek(SeekFrom::Start(entry.block_offset))?;
dest.write_all(&cluster.data)?;
// dest.write_all(&cluster.data)?;
}

progress(
Expand Down
2 changes: 1 addition & 1 deletion goldboot/src/cli/cmd/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn run(cmd: super::Commands) -> ExitCode {
match cmd {
super::Commands::Image { command } => match &command {
super::ImageCommands::List {} => {
let images = ImageLibrary::load().unwrap();
let images = ImageLibrary::find_all().unwrap();

println!("Image Name Image Size Build Date Image ID Description");
for image in images {
Expand Down
21 changes: 19 additions & 2 deletions goldboot/src/cli/cmd/liveusb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,34 @@ pub fn run(cmd: super::Commands) -> ExitCode {
return ExitCode::FAILURE;
}

// Load from library or download
let mut image_handles = match ImageLibrary::find_by_name("Test") {
Ok(image_handles) => image_handles,
Err(_) => return ExitCode::FAILURE,
};

// TODO prompt password
if image_handles[0].load(None).is_err() {
return ExitCode::FAILURE;
}

if !confirm {
if !Confirm::with_theme(&theme)
.with_prompt("Do you want to continue?")
.with_prompt(format!("Do you want to overwrite: {}?", dest))
.interact()
.unwrap()
{
std::process::exit(0);
}
}

ExitCode::SUCCESS
match image_handles[0].write(dest, ProgressBar::Write.new_empty()) {
Err(err) => {
error!(error = %err, "Failed to write image");
ExitCode::FAILURE
}
_ => ExitCode::SUCCESS,
}
}
_ => panic!(),
}
Expand Down
91 changes: 89 additions & 2 deletions goldboot/src/foundry/molds/goldboot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ use anyhow::{bail, Result};
use dialoguer::theme::Theme;
use goldboot_image::ImageArch;
use serde::{Deserialize, Serialize};
use std::io::{BufRead, BufReader};
use serde_json::{Map, Value};
use std::{
collections::HashMap,
io::{BufRead, BufReader},
};
use tracing::debug;
use validator::Validate;

use crate::{
Expand Down Expand Up @@ -54,6 +59,10 @@ impl CastImage for Goldboot {
let mut qemu = QemuBuilder::new(&worker, OsCategory::Linux)
.vga("cirrus")
.source(&worker.element.source)?
.drive_files(HashMap::from([(
"goldboot".to_string(),
get_latest_release(OsCategory::Linux, worker.arch)?,
)]))?
.start()?;

// Start HTTP
Expand All @@ -77,7 +86,8 @@ impl CastImage for Goldboot {
enter!("root"),
enter!("r00tme"),
// Install goldboot
enter!(format!("curl -o /usr/bin/goldboot https://github.com/fossable/goldboot/releases/download/goldboot-v0.0.7/goldboot_{}-unknown-linux-gnu", worker.arch)),
enter!("mount /dev/vdb /mnt"),
enter!("cp /mnt/goldboot /usr/bin/goldboot"),
enter!("chmod +x /usr/bin/goldboot"),
// Skip getty login
enter!("sed -i 's|ExecStart=.*$|ExecStart=/usr/bin/goldboot|' /usr/lib/systemd/system/[email protected]"),
Expand All @@ -89,3 +99,80 @@ impl CastImage for Goldboot {
Ok(())
}
}

/// Download the latest goldboot release.
fn get_latest_release(os: OsCategory, arch: ImageArch) -> Result<Vec<u8>> {
// List releases
let releases: Vec<Value> = reqwest::blocking::Client::new()
.get("https://api.github.com/repos/fossable/goldboot/releases")
.header("Accept", "application/vnd.github+json")
.header("X-GitHub-Api-Version", "2022-11-28")
.header("User-Agent", "goldboot")
.send()?
.json()?;
debug!(count = releases.len(), "Total releases");

// Match the major and minor versions against what we're currently running
let mut releases: Vec<Map<String, Value>> = releases
.into_iter()
.filter_map(|r| match r {
Value::Object(release) => match release.get("tag_name") {
Some(Value::String(name)) => {
if name.starts_with(&format!(
"goldboot-v{}.{}.",
crate::built_info::PKG_VERSION_MAJOR,
crate::built_info::PKG_VERSION_MINOR
)) {
Some(release)
} else {
None
}
}
_ => None,
},
_ => None,
})
.collect();

debug!(count = releases.len(), "Matched releases");

// Sort by patch version
releases.sort_by_key(|release| match release.get("tag_name") {
Some(Value::String(name)) => name.split(".").last().unwrap().parse::<i64>().unwrap(),
_ => todo!(),
});

// Find asset for the given arch
let asset = match releases.last().unwrap().get("assets") {
Some(Value::Array(assets)) => assets
.iter()
.filter_map(|a| match a {
Value::Object(asset) => match asset.get("name") {
Some(Value::String(name)) => {
if name.contains(&arch.as_github_string())
&& name.contains(&os.as_github_string())
{
Some(asset)
} else {
None
}
}
_ => None,
},
_ => None,
})
.last(),
_ => None,
};

// Download the asset
if let Some(asset) = asset {
debug!(asset = ?asset, "Found asset for download");
match asset.get("browser_download_url") {
Some(Value::String(url)) => Ok(reqwest::blocking::get(url)?.bytes()?.into()),
_ => todo!(),
}
} else {
bail!("No release asset found for OS/Arch combination");
}
}
16 changes: 15 additions & 1 deletion goldboot/src/foundry/qemu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,21 @@ pub enum OsCategory {
Windows,
}

impl OsCategory {
/// Convert to String representation for use in a Github release asset
pub fn as_github_string(&self) -> String {
match self {
OsCategory::Darwin => "apple",
OsCategory::Linux => "linux",
OsCategory::Windows => "windows",
}
.to_string()
}
}

/// Supported VM hardware acceleration.
pub enum Accel {
/// "Kernel VM" which requires Intel VT or AMD-V
Kvm,
/// Basically means no acceleration
Tcg,
Expand Down Expand Up @@ -363,7 +376,7 @@ impl QemuBuilder {

// Add a buffer of extra space
let mut fs_size: u64 = files.values().map(|c| c.len() as u64).sum();
fs_size += 32000;
fs_size += 320000;

debug!(
fs_path = ?fs_path,
Expand Down Expand Up @@ -391,6 +404,7 @@ impl QemuBuilder {
let root_dir = fs.root_dir();

for (path, content) in &files {
debug!(path = ?path, size = content.len(), "Copying file to temporary filesystem");
let mut file = root_dir.create_file(path)?;
file.write_all(content)?;
}
Expand Down
2 changes: 1 addition & 1 deletion goldboot/src/gui/select_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub fn init(window: &'static gtk::ApplicationWindow) {

let mut images = Vec::new();

for image in ImageLibrary::load().unwrap() {
for image in ImageLibrary::find_all().unwrap() {
images.push(image.id.clone());
image_box.append(&create_image_row(&image));
}
Expand Down
15 changes: 6 additions & 9 deletions goldboot/src/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{
fs::File,
path::{Path, PathBuf},
};
use tracing::{debug, info};
use tracing::{debug, error, info};

/// Represents the local image library.
///
Expand Down Expand Up @@ -119,33 +119,30 @@ impl ImageLibrary {

/// Find images in the library by ID.
pub fn find_by_id(image_id: &str) -> Result<ImageHandle> {
Ok(Self::load()?
Ok(Self::find_all()?
.into_iter()
.find(|image| image.id == image_id || image.id[0..12] == image_id[0..12])
.ok_or_else(|| anyhow!("Image not found"))?)
}

/// Find images in the library by name.
pub fn find_by_name(image_name: &str) -> Result<Vec<ImageHandle>> {
Ok(Self::load()?
Ok(Self::find_all()?
.into_iter()
.filter(|image| image.primary_header.name() == image_name)
.collect())
}

/// Load images present in the local image library.
pub fn load() -> Result<Vec<ImageHandle>> {
/// Find all images present in the local image library.
pub fn find_all() -> Result<Vec<ImageHandle>> {
let mut images = Vec::new();

for p in Self::open().directory.read_dir()? {
let path = p?.path();

if let Some(ext) = path.extension() {
if ext == "gb" {
match ImageHandle::open(&path) {
Ok(image) => images.push(image),
Err(error) => debug!("Failed to load image: {:?}", error),
}
images.push(ImageHandle::open(&path)?);
}
}
}
Expand Down

0 comments on commit e1cf2e3

Please sign in to comment.