Roey Darwish Dror fc42f107f3 Initial commit
2018-11-01 15:04:14 +02:00

190 lines
4.8 KiB
Rust

#[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);
}
}
}