move configs to dkl crate
This commit is contained in:
1409
Cargo.lock
generated
1409
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -30,3 +30,4 @@ cpio = "0.4.1"
|
|||||||
lz4 = "1.28.1"
|
lz4 = "1.28.1"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
sys-info = "0.9.1"
|
sys-info = "0.9.1"
|
||||||
|
dkl = { git = "https://novit.tech/direktil/dkl", version = "0.1.0" }
|
||||||
|
@ -1 +0,0 @@
|
|||||||
pub mod config;
|
|
@ -1,192 +0,0 @@
|
|||||||
use std::collections::BTreeMap as Map;
|
|
||||||
|
|
||||||
pub const TAKE_ALL: i16 = -1;
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Config {
|
|
||||||
pub anti_phishing_code: String,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub keymap: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub modules: Option<String>,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub resolv_conf: Option<String>,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub vpns: Map<String, String>,
|
|
||||||
|
|
||||||
pub networks: Vec<Network>,
|
|
||||||
|
|
||||||
pub auths: Vec<Auth>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub ssh: SSHServer,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub pre_lvm_crypt: Vec<CryptDev>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub lvm: Vec<LvmVG>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub crypt: Vec<CryptDev>,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub signer_public_key: Option<String>,
|
|
||||||
|
|
||||||
pub bootstrap: Bootstrap,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Auth {
|
|
||||||
pub name: String,
|
|
||||||
#[serde(alias = "sshKey")]
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub ssh_key: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub password: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Network {
|
|
||||||
pub name: String,
|
|
||||||
pub interfaces: Vec<NetworkInterface>,
|
|
||||||
pub script: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct NetworkInterface {
|
|
||||||
pub var: String,
|
|
||||||
pub n: i16,
|
|
||||||
pub regexps: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct SSHServer {
|
|
||||||
pub listen: String,
|
|
||||||
pub user_ca: Option<String>,
|
|
||||||
}
|
|
||||||
impl Default for SSHServer {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
listen: "[::]:22".to_string(),
|
|
||||||
user_ca: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct LvmVG {
|
|
||||||
#[serde(alias = "vg")]
|
|
||||||
pub name: String,
|
|
||||||
pub pvs: LvmPV,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub defaults: LvmLVDefaults,
|
|
||||||
|
|
||||||
pub lvs: Vec<LvmLV>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct LvmLVDefaults {
|
|
||||||
#[serde(default)]
|
|
||||||
pub fs: Filesystem,
|
|
||||||
#[serde(default)]
|
|
||||||
pub raid: Raid,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Filesystem {
|
|
||||||
Ext4,
|
|
||||||
Xfs,
|
|
||||||
Btrfs,
|
|
||||||
Other(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Filesystem {
|
|
||||||
pub fn fstype(&self) -> &str {
|
|
||||||
use Filesystem as F;
|
|
||||||
match self {
|
|
||||||
F::Ext4 => "ext4",
|
|
||||||
F::Xfs => "xfs",
|
|
||||||
F::Btrfs => "btrfs",
|
|
||||||
F::Other(t) => t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Filesystem {
|
|
||||||
fn default() -> Self {
|
|
||||||
Filesystem::Ext4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct LvmLV {
|
|
||||||
pub name: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub fs: Option<Filesystem>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub raid: Option<Raid>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub size: LvSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum LvSize {
|
|
||||||
Size(String),
|
|
||||||
Extents(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct LvmPV {
|
|
||||||
pub n: i16,
|
|
||||||
pub regexps: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct CryptDev {
|
|
||||||
pub name: String,
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub filter: DevFilter,
|
|
||||||
pub optional: Option<bool>,
|
|
||||||
}
|
|
||||||
impl CryptDev {
|
|
||||||
pub fn optional(&self) -> bool {
|
|
||||||
self.optional.unwrap_or_else(|| self.filter.is_prefix())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum DevFilter {
|
|
||||||
Dev(String),
|
|
||||||
Prefix(String),
|
|
||||||
}
|
|
||||||
impl DevFilter {
|
|
||||||
pub fn is_dev(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Dev(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn is_prefix(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Prefix(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Raid {
|
|
||||||
pub mirrors: Option<u8>,
|
|
||||||
pub stripes: Option<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Bootstrap {
|
|
||||||
pub dev: String,
|
|
||||||
pub seed: Option<String>,
|
|
||||||
}
|
|
@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use std::collections::BTreeSet as Set;
|
use std::collections::BTreeSet as Set;
|
||||||
use std::os::unix::fs::symlink;
|
use std::os::unix::fs::symlink;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::{fs, process::Command};
|
use tokio::{fs, process::Command};
|
||||||
|
|
||||||
use crate::{bootstrap::config::Config, cmd::version::version_string, dklog, input, utils};
|
use crate::{cmd::version::version_string, dklog, input, utils};
|
||||||
|
use dkl::bootstrap::Config;
|
||||||
|
|
||||||
mod bootstrap;
|
mod bootstrap;
|
||||||
mod dmcrypt;
|
mod dmcrypt;
|
||||||
@ -390,9 +391,9 @@ fn cmd_str(prog: &str, args: &[&str]) -> (String, Command) {
|
|||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
async fn child_reaper() {
|
async fn child_reaper() {
|
||||||
use nix::sys::wait::{waitpid, WaitPidFlag};
|
use nix::sys::wait::{WaitPidFlag, waitpid};
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
use tokio::signal::unix::{SignalKind, signal};
|
||||||
|
|
||||||
let Ok(mut sigs) =
|
let Ok(mut sigs) =
|
||||||
signal(SignalKind::child()).inspect_err(|e| warn!("failed to setup SIGCHLD handler: {e}"))
|
signal(SignalKind::child()).inspect_err(|e| warn!("failed to setup SIGCHLD handler: {e}"))
|
||||||
@ -416,7 +417,7 @@ async fn switch_root(root: &str) -> Result<()> {
|
|||||||
info!("killing all processes and switching root");
|
info!("killing all processes and switching root");
|
||||||
dklog::LOG.close().await;
|
dklog::LOG.close().await;
|
||||||
|
|
||||||
use nix::sys::signal::{kill, SIGKILL};
|
use nix::sys::signal::{SIGKILL, kill};
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
|
|
||||||
if let Err(e) = kill(Pid::from_raw(-1), SIGKILL) {
|
if let Err(e) = kill(Pid::from_raw(-1), SIGKILL) {
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use std::path::Path;
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs,
|
fs,
|
||||||
io::{AsyncBufReadExt, BufReader},
|
io::{AsyncBufReadExt, BufReader},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use dkl::{
|
||||||
|
self,
|
||||||
|
apply::{self, chroot, set_perms},
|
||||||
|
bootstrap::Config,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{exec, mount, retry, retry_or_ignore, try_exec};
|
use super::{exec, mount, retry, retry_or_ignore, try_exec};
|
||||||
use crate::bootstrap::config::Config;
|
use crate::utils;
|
||||||
use crate::{dkl, utils};
|
|
||||||
|
|
||||||
pub async fn bootstrap(cfg: Config) {
|
pub async fn bootstrap(cfg: Config) {
|
||||||
let verifier = retry(async || Verifier::from_config(&cfg)).await;
|
let verifier = retry(async || Verifier::from_config(&cfg)).await;
|
||||||
@ -50,7 +54,7 @@ pub async fn bootstrap(cfg: Config) {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
retry_or_ignore(async || apply_files(&sys_cfg.files, "/system").await).await;
|
retry_or_ignore(async || apply::files(&sys_cfg.files, "/system").await).await;
|
||||||
|
|
||||||
apply_groups(&sys_cfg.groups, "/system").await;
|
apply_groups(&sys_cfg.groups, "/system").await;
|
||||||
apply_users(&sys_cfg.users, "/system").await;
|
apply_users(&sys_cfg.users, "/system").await;
|
||||||
@ -77,7 +81,7 @@ impl Verifier {
|
|||||||
return Ok(Self { pubkey: None });
|
return Ok(Self { pubkey: None });
|
||||||
};
|
};
|
||||||
|
|
||||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||||
let pubkey = BASE64_STANDARD.decode(pubkey)?;
|
let pubkey = BASE64_STANDARD.decode(pubkey)?;
|
||||||
let pubkey = Some(pubkey);
|
let pubkey = Some(pubkey);
|
||||||
|
|
||||||
@ -250,47 +254,6 @@ async fn mount_system(cfg: &dkl::Config, bs_dir: &str, verifier: &Verifier) {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chroot(root: &str, path: &str) -> String {
|
|
||||||
format!("{root}/{}", path.trim_start_matches(|c| c == '/'))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn apply_files(files: &[dkl::File], root: &str) -> Result<()> {
|
|
||||||
for file in files {
|
|
||||||
let path = chroot(root, &file.path);
|
|
||||||
let path = Path::new(&path);
|
|
||||||
|
|
||||||
if let Some(parent) = path.parent() {
|
|
||||||
fs::create_dir_all(parent).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
use crate::dkl::FileKind as K;
|
|
||||||
match &file.kind {
|
|
||||||
K::Content(content) => fs::write(path, content.as_bytes()).await?,
|
|
||||||
K::Dir(true) => fs::create_dir(path).await?,
|
|
||||||
K::Dir(false) => {} // shouldn't happen, but semantic is to ignore
|
|
||||||
K::Symlink(tgt) => fs::symlink(tgt, path).await?,
|
|
||||||
}
|
|
||||||
|
|
||||||
match file.kind {
|
|
||||||
K::Symlink(_) => {}
|
|
||||||
_ => set_perms(path, file.mode).await?,
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("created {}", file.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_perms(path: impl AsRef<Path>, mode: Option<u32>) -> std::io::Result<()> {
|
|
||||||
if let Some(mode) = mode.filter(|m| *m != 0) {
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
let mode = std::fs::Permissions::from_mode(mode);
|
|
||||||
fs::set_permissions(path, mode).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn apply_groups(groups: &[dkl::Group], root: &str) {
|
async fn apply_groups(groups: &[dkl::Group], root: &str) {
|
||||||
for group in groups {
|
for group in groups {
|
||||||
let mut args = vec![root, "groupadd", "-r"];
|
let mut args = vec![root, "groupadd", "-r"];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use std::collections::BTreeSet as Set;
|
use std::collections::BTreeSet as Set;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
@ -6,11 +6,11 @@ use tokio::io::AsyncWriteExt;
|
|||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use super::{retry_or_ignore, USED_DEVS};
|
use super::{USED_DEVS, retry_or_ignore};
|
||||||
use crate::blockdev::{is_uninitialized, uninitialize};
|
use crate::blockdev::{is_uninitialized, uninitialize};
|
||||||
use crate::bootstrap::config::{CryptDev, DevFilter};
|
|
||||||
use crate::fs::walk_dir;
|
use crate::fs::walk_dir;
|
||||||
use crate::input;
|
use crate::input;
|
||||||
|
use dkl::bootstrap::{CryptDev, DevFilter};
|
||||||
|
|
||||||
pub async fn setup(devs: &[CryptDev]) {
|
pub async fn setup(devs: &[CryptDev]) {
|
||||||
if devs.is_empty() {
|
if devs.is_empty() {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{exec, retry, retry_or_ignore, USED_DEVS};
|
use super::{USED_DEVS, exec, retry, retry_or_ignore};
|
||||||
use crate::bootstrap::config::{Config, Filesystem, LvSize, LvmLV, LvmVG, TAKE_ALL};
|
|
||||||
use crate::fs::walk_dir;
|
use crate::fs::walk_dir;
|
||||||
use crate::{blockdev, lvm};
|
use crate::{blockdev, lvm};
|
||||||
|
use dkl::bootstrap::{Config, Filesystem, LvSize, LvmLV, LvmVG, TAKE_ALL};
|
||||||
|
|
||||||
pub async fn setup(cfg: &Config) {
|
pub async fn setup(cfg: &Config) {
|
||||||
if cfg.lvm.is_empty() {
|
if cfg.lvm.is_empty() {
|
||||||
|
@ -3,12 +3,12 @@ use log::{info, warn};
|
|||||||
use std::collections::BTreeSet as Set;
|
use std::collections::BTreeSet as Set;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{format_err, retry_or_ignore, Config, Result};
|
use super::{Result, format_err, retry_or_ignore};
|
||||||
use crate::{
|
use crate::{
|
||||||
bootstrap::config,
|
|
||||||
udev,
|
udev,
|
||||||
utils::{select_n_by_regex, NameAliases},
|
utils::{NameAliases, select_n_by_regex},
|
||||||
};
|
};
|
||||||
|
use dkl::bootstrap::{Config, Network};
|
||||||
|
|
||||||
pub async fn setup(cfg: &Config) {
|
pub async fn setup(cfg: &Config) {
|
||||||
if cfg.networks.is_empty() {
|
if cfg.networks.is_empty() {
|
||||||
@ -23,7 +23,7 @@ pub async fn setup(cfg: &Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_network(net: &config::Network, assigned: &mut Set<String>) -> Result<()> {
|
async fn setup_network(net: &Network, assigned: &mut Set<String>) -> Result<()> {
|
||||||
info!("setting up network {}", net.name);
|
info!("setting up network {}", net.name);
|
||||||
|
|
||||||
let netdevs = get_interfaces()?
|
let netdevs = get_interfaces()?
|
||||||
|
@ -7,7 +7,7 @@ use tokio::net;
|
|||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::retry_or_ignore;
|
use super::retry_or_ignore;
|
||||||
use crate::bootstrap::config::{Config, SSHServer};
|
use dkl::bootstrap::{Config, SSHServer};
|
||||||
|
|
||||||
pub async fn start(cfg: &Config) {
|
pub async fn start(cfg: &Config) {
|
||||||
retry_or_ignore(async || {
|
retry_or_ignore(async || {
|
||||||
|
61
src/dkl.rs
61
src/dkl.rs
@ -1,61 +0,0 @@
|
|||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Config {
|
|
||||||
pub layers: Vec<String>,
|
|
||||||
pub root_user: RootUser,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub mounts: Vec<Mount>,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub files: Vec<File>,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub groups: Vec<Group>,
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub users: Vec<User>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct RootUser {
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub password_hash: Option<String>,
|
|
||||||
pub authorized_keys: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Mount {
|
|
||||||
pub r#type: Option<String>,
|
|
||||||
pub dev: String,
|
|
||||||
pub path: String,
|
|
||||||
pub options: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Group {
|
|
||||||
pub name: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub gid: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct User {
|
|
||||||
pub name: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub uid: Option<u32>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub gid: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct File {
|
|
||||||
pub path: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub mode: Option<u32>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub kind: FileKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum FileKind {
|
|
||||||
Content(String),
|
|
||||||
Symlink(String),
|
|
||||||
Dir(bool),
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ use std::fmt::Display;
|
|||||||
use std::sync::{Arc, LazyLock};
|
use std::sync::{Arc, LazyLock};
|
||||||
use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net;
|
use tokio::net;
|
||||||
use tokio::sync::{oneshot, watch, Mutex};
|
use tokio::sync::{Mutex, oneshot, watch};
|
||||||
|
|
||||||
pub async fn read_line(prompt: impl Display) -> String {
|
pub async fn read_line(prompt: impl Display) -> String {
|
||||||
read(prompt, false).await
|
read(prompt, false).await
|
||||||
|
10
src/lib.rs
10
src/lib.rs
@ -1,11 +1,9 @@
|
|||||||
pub mod bootstrap;
|
pub mod blockdev;
|
||||||
pub mod cmd;
|
pub mod cmd;
|
||||||
|
pub mod dklog;
|
||||||
|
pub mod fs;
|
||||||
|
pub mod input;
|
||||||
pub mod lsblk;
|
pub mod lsblk;
|
||||||
pub mod lvm;
|
pub mod lvm;
|
||||||
pub mod udev;
|
pub mod udev;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod input;
|
|
||||||
pub mod blockdev;
|
|
||||||
pub mod fs;
|
|
||||||
pub mod dkl;
|
|
||||||
pub mod dklog;
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
Reference in New Issue
Block a user