Initial commit

This commit is contained in:
Roey Darwish Dror
2018-11-01 15:04:14 +02:00
commit fc42f107f3
6 changed files with 623 additions and 0 deletions

51
src/error.rs Normal file
View 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
View 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
View 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)
}
}