mirror of
https://github.com/philmmanjaro/alma.git
synced 2025-12-06 19:29:21 +01:00
Initial commit
This commit is contained in:
51
src/error.rs
Normal file
51
src/error.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use failure::{Backtrace, Context, Fail};
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
inner: Context<ErrorKind>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
|
||||
pub enum ErrorKind {
|
||||
#[fail(
|
||||
display = "Provided Path does not point to a USB device. Make sure you specify a file matching /dev/disk/by-id/usb-*"
|
||||
)]
|
||||
NotUSB,
|
||||
|
||||
#[fail(display = "Could not find {}", _0)]
|
||||
NoTool(&'static str),
|
||||
|
||||
#[fail(display = "Partitioning error")]
|
||||
Creation,
|
||||
}
|
||||
|
||||
impl Fail for Error {
|
||||
fn cause(&self) -> Option<&Fail> {
|
||||
self.inner.cause()
|
||||
}
|
||||
|
||||
fn backtrace(&self) -> Option<&Backtrace> {
|
||||
self.inner.backtrace()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt(&self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ErrorKind> for Error {
|
||||
fn from(kind: ErrorKind) -> Error {
|
||||
Error {
|
||||
inner: Context::new(kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Context<ErrorKind>> for Error {
|
||||
fn from(inner: Context<ErrorKind>) -> Error {
|
||||
Error { inner: inner }
|
||||
}
|
||||
}
|
||||
189
src/main.rs
Normal file
189
src/main.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate failure;
|
||||
extern crate simplelog;
|
||||
extern crate structopt;
|
||||
extern crate which;
|
||||
|
||||
mod error;
|
||||
mod tool;
|
||||
|
||||
use error::*;
|
||||
use failure::{Fail, ResultExt};
|
||||
use simplelog::*;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{exit, Command};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use structopt::StructOpt;
|
||||
use tool::Tool;
|
||||
|
||||
static MKINITCPIO: &'static str = "MODULES=()
|
||||
BINARIES=()
|
||||
FILES=()
|
||||
HOOKS=(base udev block filesystems keyboard fsck)";
|
||||
|
||||
#[derive(Fail, Debug)]
|
||||
#[fail(display = "Process failed")]
|
||||
pub struct ProcessFailed;
|
||||
|
||||
trait CommandExt {
|
||||
fn run(&mut self, context: ErrorKind) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
fn run(&mut self, context: ErrorKind) -> Result<(), Error> {
|
||||
let exit_status = self.spawn().context(context)?.wait().context(context)?;
|
||||
|
||||
if !exit_status.success() {
|
||||
return Err(ProcessFailed {}.context(context).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(name = "alma", about = "Arch Linux Mobile Applicance")]
|
||||
enum App {
|
||||
#[structopt(name = "create", about = "Create a new Arch Linux USB")]
|
||||
Create {
|
||||
#[structopt(parse(from_os_str))]
|
||||
disk: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
fn create(disk: PathBuf) -> Result<(), Error> {
|
||||
let sgdisk = Tool::find("sgdisk")?;
|
||||
let sync = Tool::find("sync")?;
|
||||
let partprobe = Tool::find("partprobe")?;
|
||||
let pacstrap = Tool::find("pacstrap")?;
|
||||
let arch_chroot = Tool::find("arch-chroot")?;
|
||||
let mount = Tool::find("mount")?;
|
||||
let umount = Tool::find("umount")?;
|
||||
let mkfat = Tool::find("mkfs.fat")?;
|
||||
let mkbtrfs = Tool::find("mkfs.btrfs")?;
|
||||
|
||||
if !(disk.starts_with("/dev/disk/by-id")
|
||||
&& (disk
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.filter(|ref f| f.starts_with("usb-"))
|
||||
.is_some()))
|
||||
{
|
||||
return Err(ErrorKind::NotUSB.into());
|
||||
}
|
||||
|
||||
info!("Partitioning the disk");
|
||||
sgdisk
|
||||
.execute()
|
||||
.args(&[
|
||||
"-Z",
|
||||
"-o",
|
||||
"--new=1::+10M",
|
||||
"--new=2::+500M",
|
||||
"--largest-new=3",
|
||||
"--typecode=1:EF02",
|
||||
"--typecode=2:EF00",
|
||||
]).arg(&disk)
|
||||
.run(ErrorKind::Creation)?;
|
||||
partprobe.execute().run(ErrorKind::Creation)?;
|
||||
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
|
||||
info!("Formatting filesystems");
|
||||
mkfat
|
||||
.execute()
|
||||
.arg("-F32")
|
||||
.arg(format!("{}-part2", disk.display()))
|
||||
.run(ErrorKind::Creation)?;
|
||||
mkbtrfs
|
||||
.execute()
|
||||
.arg("-f")
|
||||
.arg(format!("{}-part3", disk.display()))
|
||||
.run(ErrorKind::Creation)?;
|
||||
|
||||
info!("Mounting filesystems to /mnt/dok");
|
||||
mount
|
||||
.execute()
|
||||
.arg(format!("{}-part3", disk.display()))
|
||||
.arg("/mnt/dok")
|
||||
.run(ErrorKind::Creation)?;
|
||||
|
||||
fs::create_dir("/mnt/dok/boot").context(ErrorKind::Creation)?;
|
||||
|
||||
mount
|
||||
.execute()
|
||||
.arg(format!("{}-part2", disk.display()))
|
||||
.arg("/mnt/dok/boot")
|
||||
.run(ErrorKind::Creation)?;
|
||||
|
||||
info!("Bootstrapping system");
|
||||
pacstrap
|
||||
.execute()
|
||||
.args(&[
|
||||
"-c",
|
||||
"/mnt/dok",
|
||||
"base",
|
||||
"grub",
|
||||
"efibootmgr",
|
||||
"intel-ucode",
|
||||
"networkmanager",
|
||||
"btrfs-progs",
|
||||
]).run(ErrorKind::Creation)?;
|
||||
|
||||
arch_chroot
|
||||
.execute()
|
||||
.args(&["/mnt/dok", "systemctl", "enable", "NetworkManager"])
|
||||
.run(ErrorKind::Creation)?;
|
||||
|
||||
info!("Generating initramfs");
|
||||
fs::write("/mnt/dok/etc/mkinitcpio.conf", MKINITCPIO).context(ErrorKind::Creation)?;
|
||||
arch_chroot
|
||||
.execute()
|
||||
.args(&["/mnt/dok", "mkinitcpio", "-p", "linux"])
|
||||
.run(ErrorKind::Creation)?;
|
||||
|
||||
info!("Installing the Bootloader");
|
||||
arch_chroot
|
||||
.execute()
|
||||
.args(&["/mnt/dok", "bash", "-c"])
|
||||
.arg(format!("grub-install --target=i386-pc --boot-directory /boot {} && grub-install --target=x86_64-efi --efi-directory /boot --boot-directory /boot --removable && grub-mkconfig -o /boot/grub/grub.cfg", disk.display()))
|
||||
.run(ErrorKind::Creation)?;
|
||||
|
||||
info!("Unmounting filesystems");
|
||||
umount
|
||||
.execute()
|
||||
.args(&["/mnt/dok/boot", "/mnt/dok"])
|
||||
.run(ErrorKind::Creation)?;
|
||||
|
||||
sync.execute().run(ErrorKind::Creation)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let app = App::from_args();
|
||||
|
||||
CombinedLogger::init(vec![
|
||||
TermLogger::new(LevelFilter::Debug, Config::default()).unwrap(),
|
||||
]).unwrap();
|
||||
|
||||
let result = match app {
|
||||
App::Create { disk } => create(disk),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
exit(0);
|
||||
}
|
||||
Err(error) => {
|
||||
error!("{}", error);
|
||||
if let Some(cause) = error.cause() {
|
||||
error!(" {}", cause);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/tool.rs
Normal file
22
src/tool.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use super::error::*;
|
||||
use failure::ResultExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use which::which;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tool {
|
||||
exec: PathBuf,
|
||||
}
|
||||
|
||||
impl Tool {
|
||||
pub fn find(name: &'static str) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
exec: which(name).context(ErrorKind::NoTool(name))?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn execute(&self) -> Command {
|
||||
Command::new(&self.exec)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user