Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30cdf9456b | |||
| 19193a9560 | |||
| ee0ff1373f | |||
| ee03452591 | |||
| 7fef9eaf6c | |||
| 6a9875fad5 | |||
| 6a8805346f | |||
| acb8f28ab7 | |||
| d4ec6380f8 | |||
| 3036b2f417 | |||
| e72e6a0b3b | |||
| 2924263cb6 | |||
| 5ce212b3ef | |||
| ea49b99dbe | |||
| c5f3f220c9 | |||
| fec3cfcb57 | |||
| c8437c655c | |||
| fe3752baf9 | |||
| f29dc650b4 | |||
| 5ebf1331bb | |||
| be0e10723a | |||
| 01b211d0c5 | |||
| 0ef1ae769e | |||
| afac751118 |
Generated
+186
-672
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "init"
|
name = "init"
|
||||||
version = "2.6.0"
|
version = "2.6.10"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@@ -28,6 +28,8 @@ unix_mode = "0.1.4"
|
|||||||
sys-info = "0.9.1"
|
sys-info = "0.9.1"
|
||||||
dkl = { git = "https://novit.tech/direktil/dkl", version = "1.0.0" }
|
dkl = { git = "https://novit.tech/direktil/dkl", version = "1.0.0" }
|
||||||
openssl = "0.10.73"
|
openssl = "0.10.73"
|
||||||
reqwest = { version = "0.13.1", features = ["native-tls"] }
|
#reqwest = { version = "0.13.1", features = ["native-tls", "system-proxy"], default-features = false }
|
||||||
|
reqwest = { git = "https://github.com/mcluseau/rs-reqwest", version = "0.13.1", features = ["native-tls", "system-proxy", "socks"], default-features = false }
|
||||||
glob = "0.3.3"
|
glob = "0.3.3"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
tar = { version = "0.4.46", default-features = false }
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
from rust:1.95.0-alpine as rust
|
from rust:1.96.0-alpine as rust
|
||||||
|
|
||||||
run apk add --no-cache git musl-dev libudev-zero-dev openssl-dev cryptsetup-dev lvm2-dev clang-libs clang-dev
|
run apk add --no-cache git musl-dev libudev-zero-dev openssl-dev cryptsetup-dev lvm2-dev clang-libs clang-dev
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ run . /etc/os-release \
|
|||||||
&& wget -O- https://dl-cdn.alpinelinux.org/alpine/v${VERSION_ID%.*}/releases/x86_64/alpine-minirootfs-${VERSION_ID}-x86_64.tar.gz |tar zxv
|
&& wget -O- https://dl-cdn.alpinelinux.org/alpine/v${VERSION_ID%.*}/releases/x86_64/alpine-minirootfs-${VERSION_ID}-x86_64.tar.gz |tar zxv
|
||||||
|
|
||||||
run apk add --no-cache --update -p . musl libgcc coreutils \
|
run apk add --no-cache --update -p . musl libgcc coreutils \
|
||||||
iproute2 lvm2 lvm2-extra lvm2-dmeventd udev cryptsetup \
|
ethtool iproute2 lvm2 lvm2-extra lvm2-dmeventd udev cryptsetup \
|
||||||
e2fsprogs lsblk openssl openssh-server wireguard-tools-wg-quick \
|
e2fsprogs lsblk openssl openssh-server wireguard-tools-wg-quick \
|
||||||
&& rm -rf usr/share/apk var/cache/apk etc/motd dev/*
|
&& rm -rf usr/share/apk var/cache/apk etc/motd dev/*
|
||||||
|
|
||||||
|
|||||||
Executable
+62
@@ -0,0 +1,62 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
base_initrd=dist/initrd
|
||||||
|
flavor=dhcp
|
||||||
|
dir=tmp/$flavor-initrd
|
||||||
|
|
||||||
|
dist=dist/$flavor
|
||||||
|
uki=$dist/bootx64.efi
|
||||||
|
|
||||||
|
linux_v=6.18.34
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
mkdir -p tmp/dl $dist
|
||||||
|
|
||||||
|
linux=tmp/dl/linux-$linux_v
|
||||||
|
modules=tmp/dl/modules-$linux_v
|
||||||
|
|
||||||
|
[ -e $linux ] || curl -o $linux https://dkl.novit.io/dist/kernels/6.18.35
|
||||||
|
[ -e $modules ] || curl -o $modules https://dkl.novit.io/dist/layers/modules/6.18.35.erofs
|
||||||
|
|
||||||
|
rm -fr $dir
|
||||||
|
mkdir $dir
|
||||||
|
|
||||||
|
cpio --quiet --extract --file $base_initrd --directory $dir
|
||||||
|
|
||||||
|
cp -rv $flavor/. $dir
|
||||||
|
cp $modules $dir/modules.fs
|
||||||
|
|
||||||
|
(cd $dir && find * |cpio --create -H newc -R 0:0) >$dir.cpio
|
||||||
|
|
||||||
|
if cpio -tF $dir.cpio 2>&1 |grep bytes.of.junk; then echo "bad cpio archive"; exit 1; fi
|
||||||
|
|
||||||
|
zstd -12 -T0 -vf $dir.cpio &&
|
||||||
|
mv $dir.cpio.zst $dist/initrd
|
||||||
|
cp $linux $dist/vmlinuz
|
||||||
|
|
||||||
|
ukify build --output $uki --os-release "Direktil DHCP" \
|
||||||
|
--linux $linux --initrd $dist/initrd
|
||||||
|
|
||||||
|
MB=$(( 2**20 ))
|
||||||
|
sz=$(( ( $(stat -c %s $uki) + MB ) / MB + 2 ))
|
||||||
|
|
||||||
|
efi=$dist/efi.img
|
||||||
|
|
||||||
|
if [ -e $efi ]; then rm $efi; fi
|
||||||
|
|
||||||
|
truncate -s ${sz}M $efi
|
||||||
|
sgdisk -n 1:2048:0 -t 1:ef00 -c 1:"EFI System" $efi
|
||||||
|
|
||||||
|
offset=$(( 2048 * 512 ))
|
||||||
|
|
||||||
|
export MTOOLSRC=$(mktemp)
|
||||||
|
trap "rm -f $MTOOLSRC" exit
|
||||||
|
echo "drive e: file=\"$efi\" offset=$offset" >$MTOOLSRC
|
||||||
|
|
||||||
|
args="-i $efi@@$offset"
|
||||||
|
|
||||||
|
mformat -F e:
|
||||||
|
mmd e:EFI e:EFI/BOOT
|
||||||
|
mcopy $uki e:EFI/BOOT/BOOTX64.EFI
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
fn main() {
|
fn main() {
|
||||||
let output = Command::new("git")
|
let output = Command::new("git")
|
||||||
.args(&["rev-parse", "HEAD"])
|
.args(["rev-parse", "HEAD"])
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let git_commit = String::from_utf8(output.stdout).unwrap();
|
let git_commit = String::from_utf8(output.stdout).unwrap();
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
anti_phishing_code: "direktil<3"
|
||||||
|
|
||||||
|
modules: /modules.fs
|
||||||
|
|
||||||
|
auths:
|
||||||
|
- name: adm1@novit
|
||||||
|
sshKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICkpbU6sf4t0f6XAv9DuW3XH5iLM0AI5rc8PT2jwea1N
|
||||||
|
- name: adm2@novit
|
||||||
|
sshKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIomzqVAIqb7BedauhAo2VgbLqme5Jx/vjGUqZLoJqF
|
||||||
|
|
||||||
|
ssh:
|
||||||
|
listen: "[::]:22"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
- name: loopback
|
||||||
|
interfaces: [ { var: iface, n: 1, udev: !eq [INTERFACE, lo] } ]
|
||||||
|
script: |
|
||||||
|
ip a add 127.0.0.1/8 dev lo
|
||||||
|
ip a add ::1/128 dev lo
|
||||||
|
ip li set lo up
|
||||||
|
- name: main
|
||||||
|
interfaces:
|
||||||
|
- var: ifaces
|
||||||
|
n: -1
|
||||||
|
udev: !has ID_NET_NAME_MAC
|
||||||
|
script: |
|
||||||
|
ip link add main type bond mode active-backup
|
||||||
|
for l in $ifaces; do
|
||||||
|
ip link set $l master main
|
||||||
|
ip link set $l up
|
||||||
|
done
|
||||||
|
ip link set main up
|
||||||
|
udhcpc -b -i main
|
||||||
|
|
||||||
|
bootstrap:
|
||||||
|
dev: /dev/storage/bootstrap
|
||||||
+46
-7
@@ -1,10 +1,10 @@
|
|||||||
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::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
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, io::AsyncReadExt, process::Command};
|
||||||
|
|
||||||
use crate::{cmd::version::version_string, dklog, input, utils};
|
use crate::{cmd::version::version_string, dklog, input, utils};
|
||||||
use dkl::bootstrap::Config;
|
use dkl::bootstrap::Config;
|
||||||
@@ -154,7 +154,8 @@ use std::path::Path;
|
|||||||
|
|
||||||
async fn mount_modules(modules: &str, kernel_version: &str) -> Result<()> {
|
async fn mount_modules(modules: &str, kernel_version: &str) -> Result<()> {
|
||||||
info!("mounting modules");
|
info!("mounting modules");
|
||||||
mount(Some(modules), "/modules", "squashfs", None).await;
|
|
||||||
|
mount_ro_fs(modules, "/modules").await?;
|
||||||
|
|
||||||
fs::create_dir_all("/lib/modules").await?;
|
fs::create_dir_all("/lib/modules").await?;
|
||||||
let modules_path = &format!("/modules/lib/modules/{kernel_version}");
|
let modules_path = &format!("/modules/lib/modules/{kernel_version}");
|
||||||
@@ -166,6 +167,13 @@ async fn mount_modules(modules: &str, kernel_version: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
symlink(modules_path, format!("/lib/modules/{kernel_version}"))?;
|
symlink(modules_path, format!("/lib/modules/{kernel_version}"))?;
|
||||||
|
|
||||||
|
let firmware_path = &format!("/modules/lib/firmware/{kernel_version}");
|
||||||
|
if std::fs::exists(firmware_path)? {
|
||||||
|
fs::create_dir_all("/lib/firmware").await?;
|
||||||
|
symlink(firmware_path, format!("/lib/firmware/{kernel_version}"))?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +209,8 @@ async fn mount<S: AsRef<Path>>(
|
|||||||
is_file = (fs::metadata(src).await)
|
is_file = (fs::metadata(src).await)
|
||||||
.map_err(|e| format_err!("stat {src} failed: {e}"))?
|
.map_err(|e| format_err!("stat {src} failed: {e}"))?
|
||||||
.is_file();
|
.is_file();
|
||||||
|
|
||||||
|
#[allow(clippy::single_match)] // may be extended later
|
||||||
match fstype {
|
match fstype {
|
||||||
"ext4" => {
|
"ext4" => {
|
||||||
exec("fsck.ext4", &["-p", src]).await;
|
exec("fsck.ext4", &["-p", src]).await;
|
||||||
@@ -236,6 +246,35 @@ async fn mount<S: AsRef<Path>>(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn mount_ro_fs(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
|
||||||
|
let src = src.as_ref();
|
||||||
|
let dst = dst.as_ref();
|
||||||
|
|
||||||
|
// identify RO fs type
|
||||||
|
let mut buf = [0u8; 1028];
|
||||||
|
|
||||||
|
fs::File::open(src)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format_err!("open {}: {e}", src.display()))?
|
||||||
|
.read_exact(&mut buf)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format_err!("read {}: {e}", src.display()))?;
|
||||||
|
|
||||||
|
let fstype = if buf[1024..1028] == 0xE0F5E1E2u32.to_le_bytes() {
|
||||||
|
"erofs"
|
||||||
|
} else {
|
||||||
|
"squashfs"
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = fs::create_dir_all(dst).await {
|
||||||
|
error!("failed to create dir {dst}: {e}", dst = dst.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cmd = Command::new("mount");
|
||||||
|
cmd.args(["-t", fstype]).arg(src).arg(dst);
|
||||||
|
try_exec_cmd(cmd).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn start_daemon(prog: &str, args: &[&str]) {
|
async fn start_daemon(prog: &str, args: &[&str]) {
|
||||||
let (cmd_str, mut cmd) = cmd_str(prog, args);
|
let (cmd_str, mut cmd) = cmd_str(prog, args);
|
||||||
retry_or_ignore(async || {
|
retry_or_ignore(async || {
|
||||||
@@ -246,7 +285,7 @@ async fn start_daemon(prog: &str, args: &[&str]) {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_exec_cmd(mut cmd: tokio::process::Command) -> Result<()> {
|
async fn try_exec_cmd(mut cmd: Command) -> Result<()> {
|
||||||
info!(
|
info!(
|
||||||
"# {} {}",
|
"# {} {}",
|
||||||
cmd.as_std().get_program().to_string_lossy(),
|
cmd.as_std().get_program().to_string_lossy(),
|
||||||
@@ -348,9 +387,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}"))
|
||||||
@@ -368,7 +407,7 @@ async fn switch_root(root: &str) -> Result<Infallible> {
|
|||||||
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) {
|
||||||
|
|||||||
+105
-47
@@ -1,9 +1,9 @@
|
|||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use log::{info, warn};
|
use log::{debug, info, warn};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs,
|
fs,
|
||||||
io::{AsyncBufReadExt, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader},
|
io::{AsyncBufReadExt, AsyncWrite, AsyncWriteExt, BufReader},
|
||||||
};
|
};
|
||||||
|
|
||||||
use dkl::{
|
use dkl::{
|
||||||
@@ -13,7 +13,7 @@ use dkl::{
|
|||||||
bootstrap::Config,
|
bootstrap::Config,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{exec, mount, retry, retry_or_ignore, try_exec, try_exec_cmd};
|
use super::{exec, mount, mount_ro_fs, retry, retry_or_ignore, try_exec, try_exec_cmd};
|
||||||
use crate::{fs::walk_dir, utils};
|
use crate::{fs::walk_dir, utils};
|
||||||
|
|
||||||
pub async fn bootstrap(cfg: Config) {
|
pub async fn bootstrap(cfg: Config) {
|
||||||
@@ -49,7 +49,7 @@ pub async fn bootstrap(cfg: Config) {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let sys_cfg: dkl::Config = retry(async || {
|
let sys_cfg: dkl::Config = retry(async || {
|
||||||
let sys_cfg_bytes = seed_config(base_dir, &bs.seed, &verifier).await?;
|
let sys_cfg_bytes = seed_config(base_dir, bs, &verifier).await?;
|
||||||
Ok(serde_yaml::from_slice(&sys_cfg_bytes)?)
|
Ok(serde_yaml::from_slice(&sys_cfg_bytes)?)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
@@ -79,7 +79,30 @@ pub async fn bootstrap(cfg: Config) {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
exec("chroot", &["/system", "update-ca-certificates"]).await
|
exec("chroot", &["/system", "update-ca-certificates"]).await;
|
||||||
|
|
||||||
|
// activate ttyS* consoles as needed
|
||||||
|
retry_or_ignore(async || {
|
||||||
|
const PATH: &str = "/system/etc/inittab";
|
||||||
|
let mut inittab = fs::read_to_string(PATH).await?;
|
||||||
|
let mut changed = false;
|
||||||
|
for opt in utils::cmdline().filter_map(|s| s.strip_prefix("console=ttyS")) {
|
||||||
|
info!("inittab: adding entry for ttyS{opt}");
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
let mut params = opt.split(',');
|
||||||
|
let num = params.next().unwrap();
|
||||||
|
let speed = params.next().unwrap_or("115200");
|
||||||
|
inittab.push_str(&format!(
|
||||||
|
"S{num}:12345:respawn:/sbin/agetty --noclear {speed} ttyS{num} linux\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
fs::write(PATH, inittab.as_bytes()).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Verifier {
|
struct Verifier {
|
||||||
@@ -94,7 +117,7 @@ impl Verifier {
|
|||||||
let pubkey = base64_decode(pubkey)?;
|
let pubkey = base64_decode(pubkey)?;
|
||||||
let pubkey = Some(pubkey);
|
let pubkey = Some(pubkey);
|
||||||
|
|
||||||
return Ok(Self { pubkey });
|
Ok(Self { pubkey })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify_path(&self, path: impl AsRef<Path>) -> Result<Vec<u8>> {
|
async fn verify_path(&self, path: impl AsRef<Path>) -> Result<Vec<u8>> {
|
||||||
@@ -129,45 +152,85 @@ impl Verifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn seed_config(
|
async fn seed_config(
|
||||||
base_dir: &str,
|
base_dir: impl Into<PathBuf>,
|
||||||
seed_url: &Option<String>,
|
bs: &dkl::bootstrap::Bootstrap,
|
||||||
verifier: &Verifier,
|
verifier: &Verifier,
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<Vec<u8>> {
|
||||||
let cfg_path = &format!("{base_dir}/config.yaml");
|
let base_dir = base_dir.into();
|
||||||
|
|
||||||
if fs::try_exists(cfg_path).await? {
|
let cfg_path = base_dir.join("config.yaml");
|
||||||
return Ok(fs::read(cfg_path).await?);
|
|
||||||
|
if fs::try_exists(&cfg_path).await? {
|
||||||
|
return verifier.verify_path(cfg_path).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bs_tar = "/bootstrap.tar";
|
let bs_tar = "/bootstrap.tar";
|
||||||
if !fs::try_exists(bs_tar).await? {
|
if !fs::try_exists(&bs_tar).await? {
|
||||||
if let Some(seed_url) = seed_url.as_ref() {
|
if bs.seed.is_none() {
|
||||||
fetch_bootstrap(seed_url, bs_tar).await?;
|
|
||||||
} else {
|
|
||||||
return Err(format_err!(
|
return Err(format_err!(
|
||||||
"no {cfg_path}, no {bs_tar} and no seed, can't bootstrap"
|
"no {}, no {bs_tar} and no seed URL, can't bootstrap",
|
||||||
|
cfg_path.display()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
fetch_bootstrap(bs, bs_tar).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
try_exec("tar", &["xf", bs_tar, "-C", base_dir]).await?;
|
let tmp_dir = base_dir.with_added_extension("new");
|
||||||
|
fs::create_dir_all(&tmp_dir).await?;
|
||||||
|
|
||||||
if !fs::try_exists(cfg_path).await? {
|
untar(bs_tar, &tmp_dir)
|
||||||
return Err(format_err!("{cfg_path} does not exist after seeding"));
|
.await
|
||||||
|
.map_err(|e| format_err!("untar failed: {e}"))?;
|
||||||
|
|
||||||
|
let cfg_path = tmp_dir.join("config.yaml");
|
||||||
|
if !fs::try_exists(&cfg_path).await? {
|
||||||
|
return Err(format_err!(
|
||||||
|
"{} does not exist after seeding",
|
||||||
|
cfg_path.display()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
verifier.verify_path(&cfg_path).await
|
let cfg_bytes = verifier.verify_path(&cfg_path).await?;
|
||||||
|
|
||||||
|
fs::rename(tmp_dir, base_dir).await?;
|
||||||
|
Ok(cfg_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_bootstrap(seed_url: &str, output_file: &str) -> Result<()> {
|
async fn fetch_bootstrap(bs: &dkl::bootstrap::Bootstrap, output_file: &str) -> Result<()> {
|
||||||
let seed_url: reqwest::Url = seed_url.parse()?;
|
let seed_url: reqwest::Url = (bs.seed.as_ref())
|
||||||
|
.ok_or(format_err!("no seed URL"))?
|
||||||
|
.parse()
|
||||||
|
.map_err(|e| format_err!("invalid seed URL: {e}"))?;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"fetching {output_file} from {}",
|
"fetching {output_file} from {}",
|
||||||
seed_url.host_str().unwrap_or("<no host>")
|
seed_url.host_str().unwrap_or("<no host>")
|
||||||
);
|
);
|
||||||
|
|
||||||
let resp = reqwest::get(seed_url).await?;
|
let mut builder = reqwest::Client::builder();
|
||||||
|
|
||||||
|
if let Some(ref proxy) = bs.seed_proxy {
|
||||||
|
debug!("using proxy {proxy}");
|
||||||
|
let proxy = reqwest::Proxy::all(proxy) //
|
||||||
|
.map_err(|e| format_err!("seed proxy setup failed: {e}"))?;
|
||||||
|
builder = builder.proxy(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref ca) = bs.seed_ca {
|
||||||
|
debug!("using custom CA certificate");
|
||||||
|
let ca = base64_decode(ca).map_err(|e| format_err!("invalid seed CA: decode: {e}"))?;
|
||||||
|
let ca = reqwest::Certificate::from_der(&ca)
|
||||||
|
.map_err(|e| format_err!("invalid seed CA: parse: {e}"))?;
|
||||||
|
builder = builder.tls_certs_only([ca]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref sn) = bs.seed_servername {
|
||||||
|
debug!("tls server name: {sn}");
|
||||||
|
builder = builder.tls_server_name(bs.seed_servername.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let req = builder.build()?.get(seed_url);
|
||||||
|
let resp = req.send().await?;
|
||||||
|
|
||||||
if !resp.status().is_success() {
|
if !resp.status().is_success() {
|
||||||
return Err(format_err!("HTTP request failed: {}", resp.status()));
|
return Err(format_err!("HTTP request failed: {}", resp.status()));
|
||||||
@@ -181,6 +244,18 @@ async fn fetch_bootstrap(seed_url: &str, output_file: &str) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn untar(arch: impl Into<PathBuf>, target: impl Into<PathBuf>) -> Result<()> {
|
||||||
|
let arch = arch.into();
|
||||||
|
let target = target.into();
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let tar = std::fs::File::open(arch)?;
|
||||||
|
let mut tar = tar::Archive::new(tar);
|
||||||
|
tar.unpack(target)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn default_root_tmpfs_opts() -> Option<String> {
|
fn default_root_tmpfs_opts() -> Option<String> {
|
||||||
let mem = sys_info::mem_info()
|
let mem = sys_info::mem_info()
|
||||||
.inspect_err(|e| warn!("failed to get system memory info, using default tmpfs size: {e}"))
|
.inspect_err(|e| warn!("failed to get system memory info, using default tmpfs size: {e}"))
|
||||||
@@ -236,7 +311,7 @@ impl LayerMounter<'_> {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| format_err!("write {}: {e}", tgt.display()))?;
|
.map_err(|e| format_err!("write {}: {e}", tgt.display()))?;
|
||||||
|
|
||||||
let dm_name = &format!("system");
|
let dm_name = "system";
|
||||||
|
|
||||||
let mut cmd = tokio::process::Command::new("veritysetup");
|
let mut cmd = tokio::process::Command::new("veritysetup");
|
||||||
cmd.arg("open")
|
cmd.arg("open")
|
||||||
@@ -265,25 +340,7 @@ impl LayerMounter<'_> {
|
|||||||
.await
|
.await
|
||||||
};
|
};
|
||||||
|
|
||||||
retry(async || {
|
retry(async || mount_ro_fs(&mount_src, &tgt_dir).await).await;
|
||||||
let mut buf = [0u8; 1028];
|
|
||||||
fs::File::open(&mount_src)
|
|
||||||
.await
|
|
||||||
.map_err(|e| format_err!("open {}: {e}", mount_src.display()))?
|
|
||||||
.read_exact(&mut buf)
|
|
||||||
.await
|
|
||||||
.map_err(|e| format_err!("read {}: {e}", mount_src.display()))?;
|
|
||||||
|
|
||||||
let fstype = if buf[1024..1028] == 0xE0F5E1E2u32.to_le_bytes() {
|
|
||||||
"erofs"
|
|
||||||
} else {
|
|
||||||
"squashfs"
|
|
||||||
};
|
|
||||||
|
|
||||||
mount(Some(&mount_src), &tgt_dir, fstype, None).await;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if !self.lower_dir.is_empty() {
|
if !self.lower_dir.is_empty() {
|
||||||
self.lower_dir.push(':');
|
self.lower_dir.push(':');
|
||||||
@@ -315,7 +372,6 @@ async fn mount_system(cfg: &dkl::Config, bs_cfg: &Config, bs_dir: &str, verifier
|
|||||||
if layer == "modules" && bs_cfg.modules.is_some() {
|
if layer == "modules" && bs_cfg.modules.is_some() {
|
||||||
continue; // take modules from initrd
|
continue; // take modules from initrd
|
||||||
}
|
}
|
||||||
|
|
||||||
mounter.mount(layer).await;
|
mounter.mount(layer).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,8 +504,10 @@ async fn mount_filesystems(mounts: &[dkl::Mount], root: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_root_user(user: &dkl::RootUser, root: &str) -> Result<()> {
|
async fn setup_root_user(user: &dkl::RootUser, root: &str) -> Result<()> {
|
||||||
if let Some(pw_hash) = user.password_hash.as_ref().filter(|v| !v.is_empty()) {
|
if let Some(pw_hash) = user.password_hash.as_ref()
|
||||||
set_user_password("root", &pw_hash, root).await?;
|
&& !pw_hash.is_empty()
|
||||||
|
{
|
||||||
|
set_user_password("root", pw_hash, root).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut authorized_keys = Vec::new();
|
let mut authorized_keys = Vec::new();
|
||||||
|
|||||||
@@ -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,7 +6,7 @@ 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::fs::walk_dir;
|
use crate::fs::walk_dir;
|
||||||
use crate::input;
|
use crate::input;
|
||||||
|
|||||||
+10
-10
@@ -1,8 +1,8 @@
|
|||||||
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::fs::walk_dir;
|
use crate::fs::walk_dir;
|
||||||
use crate::{blockdev, lvm};
|
use crate::{blockdev, lvm};
|
||||||
use dkl::bootstrap::{Config, Filesystem, LvSize, LvmLV, LvmPV, LvmVG, TAKE_ALL};
|
use dkl::bootstrap::{Config, Filesystem, LvSize, LvmLV, LvmPV, LvmVG, TAKE_ALL};
|
||||||
@@ -31,7 +31,7 @@ pub async fn setup(cfg: &Config) {
|
|||||||
if (lvs.iter()).any(|lv| lv.equal_name(vg_name, lv_name)) {
|
if (lvs.iter()).any(|lv| lv.equal_name(vg_name, lv_name)) {
|
||||||
info!("LVM LV {vg_name}/{lv_name} exists");
|
info!("LVM LV {vg_name}/{lv_name} exists");
|
||||||
} else {
|
} else {
|
||||||
retry_or_ignore(async || setup_lv(&vg, &lv).await).await;
|
retry_or_ignore(async || setup_lv(vg, lv).await).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ pub async fn setup(cfg: &Config) {
|
|||||||
|
|
||||||
for vg in &cfg.lvm {
|
for vg in &cfg.lvm {
|
||||||
for lv in &vg.lvs {
|
for lv in &vg.lvs {
|
||||||
retry_or_ignore(async || format_lv(&vg, &lv).await).await;
|
retry_or_ignore(async || format_lv(vg, lv).await).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,20 +115,20 @@ async fn setup_lv(vg: &LvmVG, lv: &LvmLV) -> Result<()> {
|
|||||||
|
|
||||||
let mut cmd = Command::new("lvcreate");
|
let mut cmd = Command::new("lvcreate");
|
||||||
cmd.arg(&vg.name);
|
cmd.arg(&vg.name);
|
||||||
cmd.args(&["--name", &lv.name]);
|
cmd.args(["--name", &lv.name]);
|
||||||
|
|
||||||
match &lv.size {
|
match &lv.size {
|
||||||
LvSize::Size(sz) => cmd.args(&["-L", sz]),
|
LvSize::Size(sz) => cmd.args(["-L", sz]),
|
||||||
LvSize::Extents(sz) => cmd.args(&["-l", sz]),
|
LvSize::Extents(sz) => cmd.args(["-l", sz]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let raid = lv.raid.as_ref().unwrap_or(&vg.defaults.raid);
|
let raid = lv.raid.as_ref().unwrap_or(&vg.defaults.raid);
|
||||||
|
|
||||||
if let Some(mirrors) = raid.mirrors {
|
if let Some(mirrors) = raid.mirrors {
|
||||||
cmd.args(&["--mirrors", &mirrors.to_string()]);
|
cmd.args(["--mirrors", &mirrors.to_string()]);
|
||||||
}
|
}
|
||||||
if let Some(stripes) = raid.stripes {
|
if let Some(stripes) = raid.stripes {
|
||||||
cmd.args(&["--stripes", &stripes.to_string()]);
|
cmd.args(["--stripes", &stripes.to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = cmd.status().await?;
|
let status = cmd.status().await?;
|
||||||
@@ -147,7 +147,7 @@ async fn format_lv(vg: &LvmVG, lv: &LvmLV) -> Result<()> {
|
|||||||
let name = &format!("{}/{}", vg.name, lv.name);
|
let name = &format!("{}/{}", vg.name, lv.name);
|
||||||
let dev = &format!("/dev/{name}");
|
let dev = &format!("/dev/{name}");
|
||||||
|
|
||||||
if !blockdev::is_uninitialized(&dev).await? {
|
if !blockdev::is_uninitialized(dev).await? {
|
||||||
info!("{dev} looks initialized");
|
info!("{dev} looks initialized");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ 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, Result};
|
use super::{Result, format_err, retry_or_ignore};
|
||||||
use crate::{
|
use crate::{
|
||||||
udev,
|
udev,
|
||||||
utils::{select_n_by_regex, select_n_by_udev, NameAliases},
|
utils::{NameAliases, select_n_by_regex, select_n_by_udev},
|
||||||
};
|
};
|
||||||
use dkl::bootstrap::{Config, Network};
|
use dkl::bootstrap::{Config, Network};
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ async fn setup_network(net: &Network, assigned: &mut Set<String>) -> Result<()>
|
|||||||
"net",
|
"net",
|
||||||
"INTERFACE",
|
"INTERFACE",
|
||||||
&udev_filter.clone().into(),
|
&udev_filter.clone().into(),
|
||||||
&assigned,
|
assigned,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use std::fs;
|
use std::{fs, io::Write, os::unix::fs::PermissionsExt, process::Stdio};
|
||||||
use std::io::Write;
|
use tokio::{net, process::Command};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
use std::process::Stdio;
|
|
||||||
use tokio::net;
|
|
||||||
use tokio::process::Command;
|
|
||||||
|
|
||||||
use super::retry_or_ignore;
|
use super::retry_or_ignore;
|
||||||
use dkl::bootstrap::{Config, SSHServer};
|
use dkl::bootstrap::{Config, SSHServer};
|
||||||
@@ -60,7 +56,7 @@ async fn handle_ssh_connections(listener: net::TcpListener, cfg: SSHServer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for opt in &options {
|
for opt in &options {
|
||||||
sshd_args.extend(["-o", &opt]);
|
sshd_args.extend(["-o", opt]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut keygen_done = false;
|
let mut keygen_done = false;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::input;
|
|||||||
pub async fn run() {
|
pub async fn run() {
|
||||||
tokio::spawn(async {
|
tokio::spawn(async {
|
||||||
// give a bit of time for stdout
|
// give a bit of time for stdout
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{Duration, sleep};
|
||||||
sleep(Duration::from_millis(200)).await;
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
|
||||||
if let Err(e) = input::forward_requests_from_socket().await {
|
if let Err(e) = input::forward_requests_from_socket().await {
|
||||||
|
|||||||
+9
-8
@@ -1,10 +1,9 @@
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::{LazyLock, Mutex};
|
use std::sync::{LazyLock, Mutex};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use tokio::sync::watch;
|
use tokio::{io::AsyncWriteExt, sync::watch, task::JoinSet};
|
||||||
use tokio::task::JoinSet;
|
|
||||||
|
|
||||||
pub static LOG: LazyLock<Log> = LazyLock::new(Log::new);
|
pub static LOG: LazyLock<Log> = LazyLock::new(Log::default);
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
log::set_logger(&*LOG).expect("set_logger should not fail");
|
log::set_logger(&*LOG).expect("set_logger should not fail");
|
||||||
@@ -19,8 +18,8 @@ pub struct Log {
|
|||||||
tasks: Mutex<Option<JoinSet<()>>>,
|
tasks: Mutex<Option<JoinSet<()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Log {
|
impl Default for Log {
|
||||||
pub fn new() -> Self {
|
fn default() -> Self {
|
||||||
let (tx, rx) = watch::channel(0);
|
let (tx, rx) = watch::channel(0);
|
||||||
Self {
|
Self {
|
||||||
start: SystemTime::now(),
|
start: SystemTime::now(),
|
||||||
@@ -30,7 +29,9 @@ impl Log {
|
|||||||
tasks: Mutex::new(Some(JoinSet::new())),
|
tasks: Mutex::new(Some(JoinSet::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Log {
|
||||||
pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
|
pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
|
||||||
if let Some(tasks) = self.tasks.lock().unwrap().as_mut() {
|
if let Some(tasks) = self.tasks.lock().unwrap().as_mut() {
|
||||||
tasks.spawn(task);
|
tasks.spawn(task);
|
||||||
@@ -47,7 +48,6 @@ impl Log {
|
|||||||
|
|
||||||
pub async fn copy_to<W: tokio::io::AsyncWrite + Unpin>(&self, mut out: W) {
|
pub async fn copy_to<W: tokio::io::AsyncWrite + Unpin>(&self, mut out: W) {
|
||||||
let mut log = self.subscribe();
|
let mut log = self.subscribe();
|
||||||
use tokio::io::AsyncWriteExt;
|
|
||||||
while let Some(chunk) = log.next().await {
|
while let Some(chunk) = log.next().await {
|
||||||
let _ = out.write_all(&chunk).await;
|
let _ = out.write_all(&chunk).await;
|
||||||
let _ = out.flush().await;
|
let _ = out.flush().await;
|
||||||
@@ -56,7 +56,8 @@ impl Log {
|
|||||||
|
|
||||||
pub async fn close(&self) {
|
pub async fn close(&self) {
|
||||||
self.tx.lock().unwrap().take();
|
self.tx.lock().unwrap().take();
|
||||||
if let Some(tasks) = self.tasks.lock().unwrap().take() {
|
let tasks = self.tasks.lock().unwrap().take();
|
||||||
|
if let Some(tasks) = tasks {
|
||||||
tasks.join_all().await;
|
tasks.join_all().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +116,7 @@ pub struct LogWatch<'t> {
|
|||||||
impl<'t> LogWatch<'t> {
|
impl<'t> LogWatch<'t> {
|
||||||
pub async fn next(&mut self) -> Option<Vec<u8>> {
|
pub async fn next(&mut self) -> Option<Vec<u8>> {
|
||||||
loop {
|
loop {
|
||||||
let new_pos = self.rx.borrow_and_update().clone();
|
let new_pos = *self.rx.borrow_and_update();
|
||||||
if new_pos <= self.pos {
|
if new_pos <= self.pos {
|
||||||
if self.rx.changed().await.is_err() {
|
if self.rx.changed().await.is_err() {
|
||||||
return None; // finished
|
return None; // finished
|
||||||
|
|||||||
+8
-7
@@ -14,7 +14,7 @@ pub async fn read_password(prompt: impl Display) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn choice_char(s: &str) -> char {
|
fn choice_char(s: &str) -> char {
|
||||||
s.chars().skip_while(|c| *c != '[').skip(1).next().unwrap()
|
s.chars().skip_while(|c| *c != '[').nth(1).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -51,7 +51,7 @@ pub async fn read_choice<const N: usize>(choices: [&str; N]) -> char {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let line = read_line(&prompt).await;
|
let line = read_line(&prompt).await;
|
||||||
let Some(ch) = line.chars().nth(0) else {
|
let Some(ch) = line.chars().next() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,8 +70,9 @@ pub struct InputRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type Reply = Arc<Mutex<Option<oneshot::Sender<String>>>>;
|
pub type Reply = Arc<Mutex<Option<oneshot::Sender<String>>>>;
|
||||||
|
type RequestSender = watch::Sender<Option<(InputRequest, Reply)>>;
|
||||||
|
|
||||||
static REQ: LazyLock<Mutex<watch::Sender<Option<(InputRequest, Reply)>>>> = LazyLock::new(|| {
|
static REQ: LazyLock<Mutex<RequestSender>> = LazyLock::new(|| {
|
||||||
let (tx, _) = watch::channel(None);
|
let (tx, _) = watch::channel(None);
|
||||||
Mutex::new(tx)
|
Mutex::new(tx)
|
||||||
});
|
});
|
||||||
@@ -117,7 +118,7 @@ pub async fn answer_requests_from_stdin() {
|
|||||||
if req.hide {
|
if req.hide {
|
||||||
match termios::Termios::from_fd(0) {
|
match termios::Termios::from_fd(0) {
|
||||||
Ok(mut tio) => {
|
Ok(mut tio) => {
|
||||||
saved_termios = Some(tio.clone());
|
saved_termios = Some(tio);
|
||||||
tio.c_lflag &= !termios::ECHO;
|
tio.c_lflag &= !termios::ECHO;
|
||||||
if let Err(e) = termios::tcsetattr(0, termios::TCSAFLUSH, &tio) {
|
if let Err(e) = termios::tcsetattr(0, termios::TCSAFLUSH, &tio) {
|
||||||
warn!("password may be echoed! {e}");
|
warn!("password may be echoed! {e}");
|
||||||
@@ -160,13 +161,13 @@ pub async fn answer_requests_from_stdin() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// restore term if input was hidden
|
// restore term if input was hidden
|
||||||
if let Some(tio) = saved_termios {
|
if let Some(tio) = saved_termios
|
||||||
if let Err(e) = termios::tcsetattr(0, termios::TCSAFLUSH, &tio) {
|
&& let Err(e) = termios::tcsetattr(0, termios::TCSAFLUSH, &tio)
|
||||||
|
{
|
||||||
warn!("failed to restore pty attrs: {e}");
|
warn!("failed to restore pty attrs: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const SOCKET_PATH: &str = "/run/init.sock";
|
const SOCKET_PATH: &str = "/run/init.sock";
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
use eyre::{Result, format_err};
|
use eyre::{format_err, Result};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
@@ -55,7 +55,7 @@ pub struct LV {
|
|||||||
|
|
||||||
impl LV {
|
impl LV {
|
||||||
pub fn equal_name(&self, vg_name: &str, lv_name: &str) -> bool {
|
pub fn equal_name(&self, vg_name: &str, lv_name: &str) -> bool {
|
||||||
vg_name == &self.vg_name && lv_name == &self.lv_name
|
vg_name == self.vg_name && lv_name == self.lv_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ async fn report_cmd<T>(cmd: &str, find: fn(ReportObj) -> Option<Vec<T>>) -> Resu
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr.trim_ascii());
|
let stderr = String::from_utf8_lossy(output.stderr.trim_ascii());
|
||||||
return Err(format_err!("{cmd} failed: {}", stderr));
|
return Err(format_err!("{cmd} failed: {}", stderr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-15
@@ -1,6 +1,8 @@
|
|||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
|
use dkl::bootstrap::UdevFilter;
|
||||||
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
sysname: String,
|
sysname: String,
|
||||||
output: String,
|
output: String,
|
||||||
@@ -41,7 +43,7 @@ pub fn get_devices(class: &str) -> Result<Vec<Device>> {
|
|||||||
let path = path.to_string_lossy();
|
let path = path.to_string_lossy();
|
||||||
|
|
||||||
let output = std::process::Command::new("udevadm")
|
let output = std::process::Command::new("udevadm")
|
||||||
.args(&["info", &format!("--path={path}")])
|
.args(["info", &format!("--path={path}")])
|
||||||
.stderr(std::process::Stdio::piped())
|
.stderr(std::process::Stdio::piped())
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
@@ -102,10 +104,7 @@ pub struct Devs {
|
|||||||
|
|
||||||
impl<'t> Devs {
|
impl<'t> Devs {
|
||||||
pub fn iter(&'t self) -> impl Iterator<Item = Dev<'t>> {
|
pub fn iter(&'t self) -> impl Iterator<Item = Dev<'t>> {
|
||||||
self.data
|
self.data.split("\n\n").filter(|s| !s.is_empty()).map(Dev)
|
||||||
.split("\n\n")
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
.map(|s| Dev(s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn of_subsystem(&'t self, subsystem: &str) -> impl Iterator<Item = Dev<'t>> {
|
pub fn of_subsystem(&'t self, subsystem: &str) -> impl Iterator<Item = Dev<'t>> {
|
||||||
@@ -174,7 +173,7 @@ pub enum Filter {
|
|||||||
False,
|
False,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t> Filter {
|
impl Filter {
|
||||||
pub fn matches(&self, dev: &Dev) -> bool {
|
pub fn matches(&self, dev: &Dev) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::False => false,
|
Self::False => false,
|
||||||
@@ -190,21 +189,22 @@ impl<'t> Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t> Into<Filter> for dkl::bootstrap::UdevFilter {
|
impl From<UdevFilter> for Filter {
|
||||||
fn into(self) -> Filter {
|
fn from(filter: UdevFilter) -> Filter {
|
||||||
match self {
|
use UdevFilter::*;
|
||||||
Self::Has(p) => Filter::Has(p),
|
match filter {
|
||||||
Self::Eq(p, v) => Filter::Eq(p, v),
|
Has(p) => Filter::Has(p),
|
||||||
Self::Glob(p, pattern) => match glob::Pattern::new(&pattern) {
|
Eq(p, v) => Filter::Eq(p, v),
|
||||||
|
Glob(p, pattern) => match glob::Pattern::new(&pattern) {
|
||||||
Ok(pattern) => Filter::Glob(p, pattern),
|
Ok(pattern) => Filter::Glob(p, pattern),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("pattern {pattern:?} will never match: {e}");
|
warn!("pattern {pattern:?} will never match: {e}");
|
||||||
Filter::False
|
Filter::False
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Self::And(ops) => Filter::And(ops.into_iter().map(Self::into).collect()),
|
And(ops) => Filter::And(ops.into_iter().map(|op| op.into()).collect()),
|
||||||
Self::Or(ops) => Filter::Or(ops.into_iter().map(Self::into).collect()),
|
Or(ops) => Filter::Or(ops.into_iter().map(|op| op.into()).collect()),
|
||||||
Self::Not(op) => Filter::Not(Box::new((*op).into())),
|
Not(op) => Filter::Not(Box::new((*op).into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -11,7 +11,7 @@ static CMDLINE: LazyLock<String> = LazyLock::new(|| {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
});
|
});
|
||||||
|
|
||||||
fn cmdline() -> impl Iterator<Item = &'static str> {
|
pub fn cmdline() -> impl Iterator<Item = &'static str> {
|
||||||
CMDLINE.split_ascii_whitespace()
|
CMDLINE.split_ascii_whitespace()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ impl NameAliases {
|
|||||||
|
|
||||||
pub fn select_n_by_regex<'t>(
|
pub fn select_n_by_regex<'t>(
|
||||||
n: i16,
|
n: i16,
|
||||||
regexs: &Vec<String>,
|
regexs: &[String],
|
||||||
nas: impl Iterator<Item = &'t NameAliases>,
|
nas: impl Iterator<Item = &'t NameAliases>,
|
||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
// compile regexs
|
// compile regexs
|
||||||
@@ -91,7 +91,7 @@ pub fn select_n_by_regex<'t>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_n_by_udev<'t>(
|
pub async fn select_n_by_udev(
|
||||||
n: i16,
|
n: i16,
|
||||||
subsystem: &str,
|
subsystem: &str,
|
||||||
result_property: &str,
|
result_property: &str,
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ lvm:
|
|||||||
#- dev: /dev/storage/bootstrap
|
#- dev: /dev/storage/bootstrap
|
||||||
#- dev: /dev/storage/dls
|
#- dev: /dev/storage/dls
|
||||||
|
|
||||||
signer_public_key: 'MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBe6Y3zGQUIHvVXoS5GI8irY8yoB0ozFpzn/cUykA46TkHdJ8xCEaaM1MpqMrfWgDtP/rA2KeE9HjVerLnEFD01uUAUh4/OYgCBDYJPhridVDoC78KOJpkWBj7Shl0Rp0AtETvatNPa1RRe15V7nDF/Nm75Y6O3IL29lYPQ6jqEGhR810='
|
signer_public_key: 'MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAd5sR4NqLtjSt8ESNlYWvuufYj7v+aYGDlgxQThcKbzDPVe639IfH94hHE0l9TAfyU94qtN/GpFyKJ68F/u2pu70A/umT1m24ELFDqXlQXqhTsH91r+nYUZ7due3EqSrvru/yjchNNRkpoCCu3QkDF25KnrYfWWHqj9ZIRlBTCJE9SwM='
|
||||||
bootstrap:
|
bootstrap:
|
||||||
dev: /dev/storage/bootstrap
|
dev: /dev/storage/bootstrap
|
||||||
seed: http://192.168.12.254:7606/public/download-set/host/m1/bootstrap.tar?set=ICIXKJJWA6U4RQESD3KQMWO3IBW6THG4FJUM2HUNFPTIODVSXGDPXTCHSFT6IOUZO6LBAG65QIGYUMIZA3TEHTPB6BXKUFONNUWKUWAJAQRE2GDEOC4RWAAAQA3DSZJXMNSDGN34NA5G2MJ2MJXW65DTORZGC4BOORQXEAAAAAACMICVFM
|
seed: http://192.168.12.254:7606/public/download-set/host/m1/bootstrap.tar?set=ICM5KUZDRAMJPMO5OWW6PSIFYF4AHMYLAQSBZVFUDNG4DQDEW6UFQQJQKMGIXPI4CFOZFVA4CXULRXCAHKX3WELVAYS246FM6SGSGHIOAQRE2GDEOC4RUAAAQA3GEZDFMUZDOMD4NA5CUOTCN5XXI43UOJQXALTUMFZAAAAAACHHUMRU
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user