Compare commits
10 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
| c56da21eab | |||
| ed6ffcdee1 | |||
| 4d8c9540d0 | |||
| 56e87a62cf | |||
| 9769ae409e | |||
| 3abbbe9abd | |||
| b2f510e679 | |||
| a503b2a36f | |||
| f0a8e704c0 | |||
| 187129d314 |
Generated
+32
-30
@@ -512,9 +512,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.62"
|
version = "1.2.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
|
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"jobserver",
|
"jobserver",
|
||||||
@@ -1804,6 +1804,8 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rand 0.10.1",
|
"rand 0.10.1",
|
||||||
"rust-i18n",
|
"rust-i18n",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"time-format",
|
"time-format",
|
||||||
"toml 1.1.2+spec-1.1.0",
|
"toml 1.1.2+spec-1.1.0",
|
||||||
]
|
]
|
||||||
@@ -1870,14 +1872,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.16"
|
version = "0.1.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
|
checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
"libc",
|
"libc",
|
||||||
"plain",
|
"plain",
|
||||||
"redox_syscall 0.7.5",
|
"redox_syscall 0.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1955,9 +1957,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
@@ -2753,7 +2755,7 @@ version = "3.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
|
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toml_edit 0.25.11+spec-1.1.0",
|
"toml_edit 0.25.12+spec-1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2995,9 +2997,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.7.5"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b"
|
checksum = "7c7591fa2c6b601dfcfe5f043f65a1c39fcdf50efefcd7f1572e538c1f4b398d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
]
|
]
|
||||||
@@ -3276,9 +3278,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
@@ -3674,9 +3676,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.25.11+spec-1.1.0"
|
version = "0.25.12+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b"
|
checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"toml_datetime 1.1.1+spec-1.1.0",
|
"toml_datetime 1.1.1+spec-1.1.0",
|
||||||
@@ -3817,9 +3819,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.23.1"
|
version = "1.23.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
|
checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
@@ -4546,9 +4548,9 @@ checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus"
|
name = "zbus"
|
||||||
version = "5.15.0"
|
version = "5.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1"
|
checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"async-executor",
|
"async-executor",
|
||||||
@@ -4581,9 +4583,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus_macros"
|
name = "zbus_macros"
|
||||||
version = "5.15.0"
|
version = "5.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff"
|
checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -4613,18 +4615,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.48"
|
version = "0.8.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
|
checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.48"
|
version = "0.8.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4663,9 +4665,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant"
|
name = "zvariant"
|
||||||
version = "5.11.0"
|
version = "5.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee"
|
checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"endi",
|
"endi",
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
@@ -4677,9 +4679,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant_derive"
|
name = "zvariant_derive"
|
||||||
version = "5.11.0"
|
version = "5.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda"
|
checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -4690,9 +4692,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant_utils"
|
name = "zvariant_utils"
|
||||||
version = "3.3.1"
|
version = "3.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691"
|
checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ iced = { version = "0.14", default-features = false, features = ["crisp", "linux
|
|||||||
iced_moving_picture = "0"
|
iced_moving_picture = "0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rust-i18n = "3"
|
rust-i18n = "3"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
time-format = "1.2"
|
time-format = "1.2"
|
||||||
toml = "1"
|
toml = "1"
|
||||||
|
|
||||||
|
|||||||
+224
-13
@@ -18,9 +18,9 @@
|
|||||||
Functions for working with disk layout
|
Functions for working with disk layout
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::kira_size::KiraSize;
|
||||||
use log;
|
use log;
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, io::Read, process::ExitStatus};
|
||||||
use crate::kira_size::{KiraSize};
|
|
||||||
///
|
///
|
||||||
/// size - device size in bytes
|
/// size - device size in bytes
|
||||||
/// sector size - default 4096
|
/// sector size - default 4096
|
||||||
@@ -38,6 +38,7 @@ impl std::fmt::Display for BlkDev {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlkDev {
|
impl BlkDev {
|
||||||
|
/// Create new BlkDev struct with specifyed name and size
|
||||||
pub fn new(name: String, size: KiraSize) -> Self {
|
pub fn new(name: String, size: KiraSize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name,
|
name: name,
|
||||||
@@ -45,6 +46,9 @@ impl BlkDev {
|
|||||||
sector_size: KiraSize::new_b(4096),
|
sector_size: KiraSize::new_b(4096),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create new BlkDev struct with name and size parsed from hashmap
|
||||||
|
/// that contains String key val pairs with "NAME" and "SIZE" keys
|
||||||
pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> {
|
pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> {
|
||||||
Some(Self {
|
Some(Self {
|
||||||
name: data.get("NAME")?.clone(),
|
name: data.get("NAME")?.clone(),
|
||||||
@@ -52,7 +56,13 @@ impl BlkDev {
|
|||||||
sector_size: KiraSize::new_b(4096),
|
sector_size: KiraSize::new_b(4096),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// returns dev name in a "/dev/name" form
|
||||||
|
pub fn full_name(&self) -> String {
|
||||||
|
format!("/dev/{}", self.name)
|
||||||
|
}
|
||||||
|
|
||||||
/// this is blocking function
|
/// this is blocking function
|
||||||
|
/// Lists block devices in system thru "lsbk" command
|
||||||
pub fn list_sys_blk_dev() -> Result<Vec<Self>, String> {
|
pub fn list_sys_blk_dev() -> Result<Vec<Self>, String> {
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
@@ -139,6 +149,22 @@ impl FSType {
|
|||||||
_ => Self::UNKNOWN,
|
_ => Self::UNKNOWN,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// return fs type that can be used with parted utility
|
||||||
|
pub fn to_parted_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::VFAT => "fat32",
|
||||||
|
Self::XFS => "xfs",
|
||||||
|
Self::LUKS => "luks",
|
||||||
|
Self::SWAP => "linux-swap",
|
||||||
|
Self::EXT4 => "ext4",
|
||||||
|
Self::BTRFS => "btrfs",
|
||||||
|
// workaroud as parted do not support bcachefs partition type
|
||||||
|
Self::BCACHEFS => "bcachefs",
|
||||||
|
// lets do something generic here instead of panic
|
||||||
|
Self::UNKNOWN => "linux-swap",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn part_type_to_color(t: &str) -> Color {
|
// fn part_type_to_color(t: &str) -> Color {
|
||||||
@@ -177,13 +203,7 @@ pub struct PartInfo {
|
|||||||
|
|
||||||
impl std::fmt::Display for PartInfo {
|
impl std::fmt::Display for PartInfo {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(f, "{} {} {}", &self.name, &self.fs_type, self.size)
|
||||||
f,
|
|
||||||
"{} {} {}",
|
|
||||||
&self.name,
|
|
||||||
&self.fs_type,
|
|
||||||
self.size
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +311,8 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
|
|||||||
if align_size < align {
|
if align_size < align {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"!!! align_size < align !!! align_size: {} align: {}",
|
"!!! align_size < align !!! align_size: {} align: {}",
|
||||||
align_size, align
|
align_size,
|
||||||
|
align
|
||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -316,7 +337,9 @@ impl PartLayout {
|
|||||||
|
|
||||||
pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> {
|
pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> {
|
||||||
let part_list = get_dev_part_blocking(&dev.name)?;
|
let part_list = get_dev_part_blocking(&dev.name)?;
|
||||||
let parts_size = part_list.iter().fold(KiraSize::new_b(0), |acc, part| acc + part.size);
|
let parts_size = part_list
|
||||||
|
.iter()
|
||||||
|
.fold(KiraSize::new_b(0), |acc, part| acc + part.size);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
dev: dev.clone(),
|
dev: dev.clone(),
|
||||||
empty_space: dev.size - parts_size,
|
empty_space: dev.size - parts_size,
|
||||||
@@ -392,7 +415,7 @@ impl PartLayout {
|
|||||||
)
|
)
|
||||||
.add_part_reserve(
|
.add_part_reserve(
|
||||||
swap_size,
|
swap_size,
|
||||||
FSType::XFS,
|
FSType::BTRFS,
|
||||||
Some("root".into()),
|
Some("root".into()),
|
||||||
None,
|
None,
|
||||||
Some("/".into()),
|
Some("/".into()),
|
||||||
@@ -415,6 +438,195 @@ impl PartLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes parted command with given arguments
|
||||||
|
fn exec_parted(args: &Vec<&str>) -> Result<String, String> {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
match Command::new("parted").args(args).output() {
|
||||||
|
Err(ex) => Err(ex.to_string()),
|
||||||
|
Ok(out) => {
|
||||||
|
if out.status.success() {
|
||||||
|
String::from_utf8(out.stdout).map_err(|ex| ex.to_string())
|
||||||
|
} else {
|
||||||
|
Err(out.status.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parted_get_last_part_num(dev: &BlkDev) -> Result<u32, String> {
|
||||||
|
// parted -m /dev/loop0 print
|
||||||
|
match exec_parted(&vec!["-m", dev.full_name().as_str(), "print"]) {
|
||||||
|
Ok(res) => {
|
||||||
|
match res.lines().last().and_then(|last_line| {
|
||||||
|
last_line
|
||||||
|
.split_once(':')
|
||||||
|
.and_then(|(first_part, a)| u32::from_str_radix(first_part, 10).ok())
|
||||||
|
}) {
|
||||||
|
Some(n) => Ok(n),
|
||||||
|
None => Err("Error parsing parted output".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ex) => Err(ex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// init gpt partition table
|
||||||
|
/// parted --script $install_device mklabel gpt
|
||||||
|
/// Its blocking function
|
||||||
|
pub fn parted_init_gpt_part_table(dev: &BlkDev) -> Result<(), String> {
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"mklabel",
|
||||||
|
"gpt",
|
||||||
|
])
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
const LINUX_ROOT_X86_64_TYPE: &str = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709";
|
||||||
|
|
||||||
|
/// creating partition from PartInfo specification
|
||||||
|
/// It assumes that we add partitions sequentially to empty space (for part attributes settings)
|
||||||
|
/// Otherwise can produce bonkers layout
|
||||||
|
pub fn parted_mkpart(dev: &BlkDev, part: &PartInfo) -> Result<(), String> {
|
||||||
|
let part_end = part.start + part.size;
|
||||||
|
|
||||||
|
// gpt label should be in double qutes
|
||||||
|
let gpt_label = if let Some(s) = &part.gpt_label {
|
||||||
|
format!("\"{}\"", s)
|
||||||
|
} else {
|
||||||
|
"\"\"".to_string()
|
||||||
|
};
|
||||||
|
// creating partition
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"mkpart",
|
||||||
|
gpt_label.as_str(),
|
||||||
|
part.fs_type.to_parted_str(),
|
||||||
|
part.start.to_parted_bytes_str().as_str(),
|
||||||
|
part_end.to_parted_bytes_str().as_str(),
|
||||||
|
])?;
|
||||||
|
|
||||||
|
// set partition options depending on its role
|
||||||
|
if let Some(role) = &part.role {
|
||||||
|
// getting part number of last created partition (hopefully)
|
||||||
|
let part_id = parted_get_last_part_num(dev)?;
|
||||||
|
match role {
|
||||||
|
PartRole::EFI => {
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"set",
|
||||||
|
&part_id.to_string(),
|
||||||
|
"esp",
|
||||||
|
"on",
|
||||||
|
])?;
|
||||||
|
}
|
||||||
|
// parted --script $install_device type 3 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
|
||||||
|
PartRole::ROOT => {
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"type",
|
||||||
|
&part_id.to_string(),
|
||||||
|
LINUX_ROOT_X86_64_TYPE,
|
||||||
|
])?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// creating single drive bcachefs volume
|
||||||
|
pub fn bcachefs_format(
|
||||||
|
dev: &BlkDev,
|
||||||
|
compression: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// sudo bcachefs format --compression=lz4 --background_compression=lz4 --encrypted /dev/loop0
|
||||||
|
// --data_checksum=none - do not make sense on single dev volumes
|
||||||
|
// sudo bcachefs format --force --compression=lz4 --background_compression=lz4 --encrypted --passphrase_file=./test.pass /dev/loop0
|
||||||
|
use std::fs;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
let mut arg = vec!["format", "--force", "--data_checksum=none"];
|
||||||
|
// if compression algorythm specified
|
||||||
|
if let Some(compresss_alg) = &compression {
|
||||||
|
arg.push(compresss_alg.as_str());
|
||||||
|
}
|
||||||
|
// if we want encrypted volume
|
||||||
|
if let Some(passw) = password {
|
||||||
|
// creating tmp file with password
|
||||||
|
fs::write("/tmp/bzzpsspass.txt", &passw).map_err(|ex| ex.to_string())?;
|
||||||
|
arg.push("--encrypted");
|
||||||
|
arg.push("--passphrase_file=/tmp/bzzpsspass.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add dev name at the end
|
||||||
|
let dev_name = dev.full_name();
|
||||||
|
arg.push(dev_name.as_str());
|
||||||
|
|
||||||
|
let out = Command::new("bcachefs")
|
||||||
|
.args(arg)
|
||||||
|
.output()
|
||||||
|
.map_err(|ex| ex.to_string())?;
|
||||||
|
|
||||||
|
// ignore any errors while deleting pass tmp file
|
||||||
|
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
||||||
|
|
||||||
|
if out.status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// unlok encrypted bcache volume
|
||||||
|
pub fn bcachefs_unlock(dev: &BlkDev, pass: String) -> Result<(), String> {
|
||||||
|
use std::fs;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0
|
||||||
|
let dev_name = dev.full_name();
|
||||||
|
let arg = vec!["unlock", "--file=/tmp/bzzpsspass.txt ", dev_name.as_str()];
|
||||||
|
// creating tmp file with password
|
||||||
|
fs::write("/tmp/bzzpsspass.txt", &pass).map_err(|ex| ex.to_string())?;
|
||||||
|
|
||||||
|
let out = Command::new("bcachefs")
|
||||||
|
.args(arg)
|
||||||
|
.output()
|
||||||
|
.map_err(|ex| ex.to_string())?;
|
||||||
|
// ignore any errors while deleting pass tmp file
|
||||||
|
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
||||||
|
|
||||||
|
if out.status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// create subvolume
|
||||||
|
pub fn bcachefs_create_subvolume(vol_path: &str) -> Result<(), String> {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
// sudo bcachefs subvolume create /mnt/test/nyan
|
||||||
|
let out = Command::new("bcachefs")
|
||||||
|
.args(["subvolume", "create", vol_path])
|
||||||
|
.output()
|
||||||
|
.map_err(|ex| ex.to_string())?;
|
||||||
|
if out.status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
@@ -466,4 +678,3 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ impl std::fmt::Display for KiraSize {
|
|||||||
|
|
||||||
impl KiraSize {
|
impl KiraSize {
|
||||||
pub const SUFFIXS: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"];
|
pub const SUFFIXS: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"];
|
||||||
|
|
||||||
|
/// returns size in bytes suffixed with "B" character
|
||||||
|
pub fn to_parted_bytes_str(&self)-> String {
|
||||||
|
format!("{}B", self.size_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn new_b(size:u64) -> Self {
|
pub fn new_b(size:u64) -> Self {
|
||||||
Self { size_bytes: size }
|
Self { size_bytes: size }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,3 +95,37 @@ pub fn get_gpus_list() -> Vec<GpuInfo> {
|
|||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct SecureBootStatus {
|
||||||
|
pub installed: bool,
|
||||||
|
pub guid: String,
|
||||||
|
pub setup_mode: bool,
|
||||||
|
pub secure_boot: bool,
|
||||||
|
pub vendors: Vec<String>,
|
||||||
|
pub firmware_quirks: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_secure_boot_status() -> Result<SecureBootStatus, String> {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
//sbctl --json status
|
||||||
|
match Command::new("sbctl").args(["--json", "status"]).output() {
|
||||||
|
Ok(cmd_output) => {
|
||||||
|
if cmd_output.status.success() {
|
||||||
|
match serde_json::from_slice(&cmd_output.stdout) {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(ex) => Err(ex.to_string()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"Error getting disk devices list: {}",
|
||||||
|
String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into())
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ex) => Err(ex.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+15
-2
@@ -4,7 +4,10 @@
|
|||||||
"button.next": "Next",
|
"button.next": "Next",
|
||||||
"button.back": "Back",
|
"button.back": "Back",
|
||||||
"button.cancel": "Cancel",
|
"button.cancel": "Cancel",
|
||||||
|
"button.finish": "Finish",
|
||||||
"button.exit": "Exit",
|
"button.exit": "Exit",
|
||||||
|
"button.yes": "Yes",
|
||||||
|
"button.no": "No",
|
||||||
"wellcome.text": "Welcome to Kira Installer!",
|
"wellcome.text": "Welcome to Kira Installer!",
|
||||||
"wellcome.choose_language": "Please select language to use during istalation!",
|
"wellcome.choose_language": "Please select language to use during istalation!",
|
||||||
"license.license": "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.",
|
"license.license": "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.",
|
||||||
@@ -37,7 +40,17 @@
|
|||||||
"partition.select_swapmode": "Select swap partition mode",
|
"partition.select_swapmode": "Select swap partition mode",
|
||||||
"partition.use_zram": "Use zram technology",
|
"partition.use_zram": "Use zram technology",
|
||||||
"partition.use_secure_boot": "Setup secure boot",
|
"partition.use_secure_boot": "Setup secure boot",
|
||||||
|
"partition.encrypt_drive": "Encrypt drive",
|
||||||
"partition.current_dev_layout": "Current device layout",
|
"partition.current_dev_layout": "Current device layout",
|
||||||
"partition.current_after_install": "After installation device will look like this"
|
"partition.current_after_install": "After installation device will look like this",
|
||||||
|
"security.user_name": "User login name",
|
||||||
|
"security.user_full_name": "User full name/description",
|
||||||
|
"security.user_password": "User password",
|
||||||
|
"security.reuse_user_password": "Reuse user password for root user",
|
||||||
|
"security.root_password": "Root password",
|
||||||
|
"security.pass_low": "low security",
|
||||||
|
"security.pass_middle": "normal security",
|
||||||
|
"security.pass_hight": "hight security",
|
||||||
|
"install.caption": "Now we will beee install Bzz Linux to your PC",
|
||||||
|
"install.warning": "We a ready to start installation, this action will destroy all data on select drive!\nDo you want to proceed?"
|
||||||
}
|
}
|
||||||
+48
-23
@@ -52,6 +52,7 @@ enum Views {
|
|||||||
Locale(stages::locale::LocaleStage),
|
Locale(stages::locale::LocaleStage),
|
||||||
Keyboard(stages::keyboard::KeyboardStage),
|
Keyboard(stages::keyboard::KeyboardStage),
|
||||||
Partition(stages::partition::PartitionStage),
|
Partition(stages::partition::PartitionStage),
|
||||||
|
Security(stages::security::SecurityStage),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Message {
|
enum Message {
|
||||||
@@ -63,12 +64,14 @@ enum Message {
|
|||||||
Locale(stages::locale::Message),
|
Locale(stages::locale::Message),
|
||||||
Keyboard(stages::keyboard::Message),
|
Keyboard(stages::keyboard::Message),
|
||||||
Partition(stages::partition::Message),
|
Partition(stages::partition::Message),
|
||||||
|
Security(stages::security::Message),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KiraState {
|
struct KiraState {
|
||||||
current_view: Views,
|
current_view: Views,
|
||||||
toml_config: toml::Table,
|
toml_config: toml::Table,
|
||||||
config: KiraConfig,
|
config: KiraConfig,
|
||||||
|
stages_save: Vec<Views>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONFIG_PATH_STR: &str = "kira_config.toml";
|
const CONFIG_PATH_STR: &str = "kira_config.toml";
|
||||||
@@ -95,6 +98,7 @@ impl KiraState {
|
|||||||
config: KiraConfig {
|
config: KiraConfig {
|
||||||
config_trail: Vec::new(),
|
config_trail: Vec::new(),
|
||||||
},
|
},
|
||||||
|
stages_save: Vec::new(),
|
||||||
},
|
},
|
||||||
Task::done(Message::Start),
|
Task::done(Message::Start),
|
||||||
)
|
)
|
||||||
@@ -111,6 +115,7 @@ fn view(k_state: &KiraState) -> Element<'_, Message> {
|
|||||||
Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale),
|
Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale),
|
||||||
Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard),
|
Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard),
|
||||||
Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition),
|
Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition),
|
||||||
|
Views::Security(security_stage) => security_stage.view().map(Message::Security)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +138,8 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match wlc_view.update(wlc_msg) {
|
match wlc_view.update(wlc_msg) {
|
||||||
StageAction::Next(welcome_res) => {
|
StageAction::Next(welcome_res) => {
|
||||||
k_state.config.config_trail.push(welcome_res);
|
k_state.config.config_trail.push(welcome_res);
|
||||||
k_state.current_view = Views::License(license::LicenseStage {});
|
let s_state = std::mem::replace(&mut k_state.current_view, Views::License(license::LicenseStage {}));
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Abort => iced::exit(),
|
StageAction::Abort => iced::exit(),
|
||||||
@@ -149,13 +155,13 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match action {
|
match action {
|
||||||
StageAction::Next(license_res) => {
|
StageAction::Next(license_res) => {
|
||||||
k_state.config.config_trail.push(license_res);
|
k_state.config.config_trail.push(license_res);
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(&mut k_state.current_view, Views::Network(network::NetworkStage::new(&k_state.toml_config)));
|
||||||
Views::Network(network::NetworkStage::new(&k_state.toml_config));
|
k_state.stages_save.push(s_state);
|
||||||
Task::done(Message::Network(network::Message::CheckNetwork))
|
Task::done(Message::Network(network::Message::CheckNetwork))
|
||||||
}
|
}
|
||||||
StageAction::Abort => iced::exit(),
|
StageAction::Abort => iced::exit(),
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.current_view = Views::Welcome(WelcomeStage::new());
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
k_state.config.config_trail.pop();
|
k_state.config.config_trail.pop();
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -173,12 +179,12 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
network::UpdateResult::StageAction(action) => match action {
|
network::UpdateResult::StageAction(action) => match action {
|
||||||
StageAction::Next(network_res) => {
|
StageAction::Next(network_res) => {
|
||||||
k_state.config.config_trail.push(network_res);
|
k_state.config.config_trail.push(network_res);
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(&mut k_state.current_view,Views::TimeZone(stages::timezone::TimeZoneStage::new()));
|
||||||
Views::TimeZone(stages::timezone::TimeZoneStage::new());
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.current_view = Views::License(license::LicenseStage {});
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
k_state.config.config_trail.pop();
|
k_state.config.config_trail.pop();
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -195,16 +201,15 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match action {
|
match action {
|
||||||
StageAction::Next(tz_res) => {
|
StageAction::Next(tz_res) => {
|
||||||
k_state.config.config_trail.push(tz_res);
|
k_state.config.config_trail.push(tz_res);
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(&mut k_state.current_view,Views::Locale(stages::locale::LocaleStage::new(&k_state.config)));
|
||||||
Views::Locale(stages::locale::LocaleStage::new(&k_state.config));
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Abort => iced::exit(),
|
StageAction::Abort => iced::exit(),
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.config.config_trail.pop();
|
k_state.config.config_trail.pop();
|
||||||
k_state.current_view =
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
Views::Network(network::NetworkStage::new(&k_state.toml_config));
|
Task::none()
|
||||||
Task::done(Message::Network(network::Message::CheckNetwork))
|
|
||||||
}
|
}
|
||||||
StageAction::None => Task::none(),
|
StageAction::None => Task::none(),
|
||||||
}
|
}
|
||||||
@@ -217,14 +222,13 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match locale_view.update(locale_msg) {
|
match locale_view.update(locale_msg) {
|
||||||
StageAction::Next(locale_res) => {
|
StageAction::Next(locale_res) => {
|
||||||
k_state.config.config_trail.push(locale_res);
|
k_state.config.config_trail.push(locale_res);
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(&mut k_state.current_view,Views::Keyboard(stages::keyboard::KeyboardStage::new()));
|
||||||
Views::Keyboard(stages::keyboard::KeyboardStage::new());
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.config.config_trail.pop();
|
k_state.config.config_trail.pop();
|
||||||
k_state.current_view =
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
Views::TimeZone(stages::timezone::TimeZoneStage::new());
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
@@ -240,7 +244,8 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match stages::partition::PartitionStage::new(&k_state.toml_config) {
|
match stages::partition::PartitionStage::new(&k_state.toml_config) {
|
||||||
Ok(part_stage) => {
|
Ok(part_stage) => {
|
||||||
k_state.config.config_trail.push(keyboard_res);
|
k_state.config.config_trail.push(keyboard_res);
|
||||||
k_state.current_view = Views::Partition(part_stage);
|
let s_state = std::mem::replace(&mut k_state.current_view,Views::Partition(part_stage));
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
}
|
}
|
||||||
Err(ex) => {
|
Err(ex) => {
|
||||||
log::error!("Error, cannot init partition stage: {}", ex);
|
log::error!("Error, cannot init partition stage: {}", ex);
|
||||||
@@ -250,8 +255,7 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.config.config_trail.pop();
|
k_state.config.config_trail.pop();
|
||||||
k_state.current_view =
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
Views::Locale(stages::locale::LocaleStage::new(&k_state.config));
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
@@ -259,18 +263,19 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Message::Partition(partition_msg) => {
|
Message::Partition(partition_msg) => {
|
||||||
if let Views::Partition(partition_view) = &mut k_state.current_view {
|
if let Views::Partition(partition_view) = &mut k_state.current_view {
|
||||||
match partition_view.update(partition_msg) {
|
match partition_view.update(partition_msg) {
|
||||||
StageAction::Next(partition_res) => {
|
StageAction::Next(partition_res) => {
|
||||||
k_state.config.config_trail.push(partition_res);
|
k_state.config.config_trail.push(partition_res);
|
||||||
iced::exit()
|
let s_state = std::mem::replace(&mut k_state.current_view,Views::Security(stages::security::SecurityStage::new()));
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.config.config_trail.pop();
|
k_state.config.config_trail.pop();
|
||||||
k_state.current_view =
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
Views::Keyboard(stages::keyboard::KeyboardStage::new());
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
@@ -278,7 +283,27 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Message::Security(security_msg) => {
|
||||||
|
if let Views::Security(security_view) = &mut k_state.current_view {
|
||||||
|
match security_view.update(security_msg) {
|
||||||
|
StageAction::Next(security_res) => {
|
||||||
|
k_state.config.config_trail.push(security_res);
|
||||||
|
//let s_state = std::mem::replace(&mut k_state.current_view,
|
||||||
|
Task::none()
|
||||||
}
|
}
|
||||||
|
StageAction::Back => {
|
||||||
|
k_state.config.config_trail.pop();
|
||||||
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
_ => Task::none(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ impl StageResult {
|
|||||||
self.add_val(val_name, ConfigValue::Bool(v))
|
self.add_val(val_name, ConfigValue::Bool(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_val_u64(self, val_name: &str, v: u64) -> Self {
|
||||||
|
self.add_val(val_name, ConfigValue::U64(v))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_val(&self, val_name: &str) -> Option<ConfigValue> {
|
pub fn get_val(&self, val_name: &str) -> Option<ConfigValue> {
|
||||||
let v = self
|
let v = self
|
||||||
.config
|
.config
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
// <Kira Installer - universal Linux installer.>
|
||||||
|
// Copyright (C) <2026> <Kira Foundation>
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Actual installation script.
|
||||||
|
*/
|
||||||
|
|
||||||
|
///
|
||||||
|
/// So. I am goind to make dir structure lile this
|
||||||
|
/// /bzz/root
|
||||||
|
/// /bzz/home
|
||||||
|
/// Mount root from it and binmount home
|
||||||
|
///
|
||||||
|
|
||||||
|
use log;
|
||||||
|
use crate::{
|
||||||
|
kira_theming,
|
||||||
|
stage::{KiraConfig, StageAction, StageResult},
|
||||||
|
};
|
||||||
|
use iced::{Alignment, Task, futures, widget};
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum InstallationState {
|
||||||
|
UserConfirmDialog,
|
||||||
|
Partitionning,
|
||||||
|
CopyFiles,
|
||||||
|
Configuring,
|
||||||
|
Finish,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct InstallStage {
|
||||||
|
state: InstallationState,
|
||||||
|
progress: f32,
|
||||||
|
progress_message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Message {
|
||||||
|
UserConfirms,
|
||||||
|
UserDenies,
|
||||||
|
StartPartitioning,
|
||||||
|
UpdateProgress(Result<(f32, String), String>),
|
||||||
|
PartitioningFinish(Result<(), String>),
|
||||||
|
Back,
|
||||||
|
Cancel,
|
||||||
|
Finish,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Update {
|
||||||
|
StgAct(StageAction),
|
||||||
|
Task(Task<Message>),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl InstallStage {
|
||||||
|
pub fn new(config: &KiraConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
state: InstallationState::UserConfirmDialog,
|
||||||
|
progress: 0.0,
|
||||||
|
progress_message: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_result(&self) -> StageResult {
|
||||||
|
StageResult::new("install")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, message: Message) -> Update {
|
||||||
|
match message {
|
||||||
|
Message::UserDenies => Update::StgAct(StageAction::Back),
|
||||||
|
Message::UserConfirms => {
|
||||||
|
// starting installation process here
|
||||||
|
self.state = InstallationState::Partitionning;
|
||||||
|
Update::Task(Task::run(disk_part::check_connected(), Message::UpdateProgress))
|
||||||
|
//Update::Task(Task::perform(, Message::PartitioningFinish))
|
||||||
|
}
|
||||||
|
Message::StartPartitioning => Update::StgAct(StageAction::None),
|
||||||
|
Message::PartitioningFinish(res) => Update::StgAct(StageAction::None),
|
||||||
|
Message::UpdateProgress(maybe_msg) => {
|
||||||
|
match maybe_msg {
|
||||||
|
Ok((progress, msg))=> {
|
||||||
|
self.progress = progress;
|
||||||
|
self.progress_message = msg;
|
||||||
|
Update::StgAct(StageAction::None)
|
||||||
|
}
|
||||||
|
Err(ex) => {
|
||||||
|
log::error!("{}", &ex);
|
||||||
|
self.state = InstallationState::Error;
|
||||||
|
Update::StgAct(StageAction::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Update::StgAct(StageAction::None)
|
||||||
|
|
||||||
|
},
|
||||||
|
Message::Cancel => Update::StgAct(StageAction::Abort),
|
||||||
|
Message::Back => Update::StgAct(StageAction::Back),
|
||||||
|
Message::Finish => Update::StgAct(StageAction::None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view(&self) -> iced::Element<'_, Message> {
|
||||||
|
let action_button = if self.state == InstallationState::Finish {
|
||||||
|
widget::button(widget::text(t!("button.finish"))).on_press(Message::Finish)
|
||||||
|
} else {
|
||||||
|
widget::button(widget::text(t!("button.cancel"))).on_press(Message::Cancel)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Embed the image bytes into the executable
|
||||||
|
let welcom_logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
|
||||||
|
|
||||||
|
let main_content = widget::column![
|
||||||
|
widget::container(
|
||||||
|
widget::column![
|
||||||
|
widget::image(welcom_logo_handle)
|
||||||
|
.width(iced::Pixels(128.0))
|
||||||
|
.height(iced::Pixels(128.0)),
|
||||||
|
widget::text(t!("install.caption")),
|
||||||
|
widget::progress_bar(0.0..=100.0, self.progress),
|
||||||
|
widget::text(self.progress_message.clone()),
|
||||||
|
]
|
||||||
|
.padding(10)
|
||||||
|
.spacing(10)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
)
|
||||||
|
.height(iced::Length::Fill)
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.align_y(Alignment::Center),
|
||||||
|
widget::container(action_button)
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.align_y(Alignment::End)
|
||||||
|
.padding(10),
|
||||||
|
]
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.spacing(10);
|
||||||
|
|
||||||
|
let page_stack = widget::Stack::with_capacity(2).push(main_content);
|
||||||
|
|
||||||
|
if self.state == InstallationState::UserConfirmDialog {
|
||||||
|
page_stack
|
||||||
|
.push(
|
||||||
|
widget::column![
|
||||||
|
widget::text(t!("install.warning")),
|
||||||
|
widget::row![
|
||||||
|
widget::button(widget::text(t!("button.no")))
|
||||||
|
.on_press(Message::UserDenies),
|
||||||
|
widget::space::horizontal(), // Pushes the right button to the far right
|
||||||
|
widget::button(widget::text(t!("button.yes")))
|
||||||
|
.on_press(Message::UserConfirms)
|
||||||
|
]
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.align_y(Alignment::End)
|
||||||
|
.padding(10)
|
||||||
|
]
|
||||||
|
.align_x(Alignment::Center),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
page_stack.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,3 +22,5 @@ pub mod timezone;
|
|||||||
pub mod locale;
|
pub mod locale;
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
pub mod partition;
|
pub mod partition;
|
||||||
|
pub mod security;
|
||||||
|
pub mod install;
|
||||||
+53
-21
@@ -68,6 +68,8 @@ fn part_type_to_color(t: &FSType) -> Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts PartInfo into data for ColorBar widget
|
||||||
|
///
|
||||||
fn part_to_ct_params(part: &PartInfo, dev_size: KiraSize) -> (String, u16, Color) {
|
fn part_to_ct_params(part: &PartInfo, dev_size: KiraSize) -> (String, u16, Color) {
|
||||||
let fill_ratio: u16 = ((part.size.b() as f32 / dev_size.b() as f32) * 11.0) as u16;
|
let fill_ratio: u16 = ((part.size.b() as f32 / dev_size.b() as f32) * 11.0) as u16;
|
||||||
let fill_ratio = fill_ratio.max(1);
|
let fill_ratio = fill_ratio.max(1);
|
||||||
@@ -82,6 +84,8 @@ pub struct PartitionStage {
|
|||||||
swap_mode: Option<SwapMode>,
|
swap_mode: Option<SwapMode>,
|
||||||
use_zram: bool,
|
use_zram: bool,
|
||||||
secure_boot: bool,
|
secure_boot: bool,
|
||||||
|
encrypt_drive: bool,
|
||||||
|
secure_boot_in_setup_mode: bool,
|
||||||
custom_swap_size_mb: u32,
|
custom_swap_size_mb: u32,
|
||||||
custom_swap_size_str: String,
|
custom_swap_size_str: String,
|
||||||
selected_disk_parts: Option<Vec<(String, u16, Color)>>,
|
selected_disk_parts: Option<Vec<(String, u16, Color)>>,
|
||||||
@@ -96,6 +100,7 @@ pub enum Message {
|
|||||||
SelectSwapMode(SwapMode),
|
SelectSwapMode(SwapMode),
|
||||||
ToggleZram(bool),
|
ToggleZram(bool),
|
||||||
ToggleSecureBoot(bool),
|
ToggleSecureBoot(bool),
|
||||||
|
ToggleEncrypDrive(bool),
|
||||||
SwapSizeFieldChange(String),
|
SwapSizeFieldChange(String),
|
||||||
SwapSizeIncrement,
|
SwapSizeIncrement,
|
||||||
SwapSizeDecrement,
|
SwapSizeDecrement,
|
||||||
@@ -116,6 +121,14 @@ impl PartitionStage {
|
|||||||
|
|
||||||
let min_disk_size: KiraSize = KiraSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192));
|
let min_disk_size: KiraSize = KiraSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192));
|
||||||
|
|
||||||
|
|
||||||
|
let sec_boot_setup = match kira_sysinfo::get_secure_boot_status() {
|
||||||
|
Ok(sb_status) => sb_status.setup_mode,
|
||||||
|
Err(ex) => {
|
||||||
|
log::error!("Unable to get secure boot status! {}", ex);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
match maybe_dev_list {
|
match maybe_dev_list {
|
||||||
Ok(dev_list) => {
|
Ok(dev_list) => {
|
||||||
let acepted_dev: Vec<BlkDev> = dev_list
|
let acepted_dev: Vec<BlkDev> = dev_list
|
||||||
@@ -143,6 +156,8 @@ impl PartitionStage {
|
|||||||
swap_mode: Some(SwapMode::SwapNoHibernate),
|
swap_mode: Some(SwapMode::SwapNoHibernate),
|
||||||
use_zram: true,
|
use_zram: true,
|
||||||
secure_boot: false,
|
secure_boot: false,
|
||||||
|
encrypt_drive: false,
|
||||||
|
secure_boot_in_setup_mode: sec_boot_setup,
|
||||||
custom_swap_size_mb: 1024,
|
custom_swap_size_mb: 1024,
|
||||||
custom_swap_size_str: "1024MB".into(),
|
custom_swap_size_str: "1024MB".into(),
|
||||||
selected_disk_parts: None,
|
selected_disk_parts: None,
|
||||||
@@ -205,14 +220,14 @@ impl PartitionStage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gen_result(&self) -> StageResult {
|
fn gen_result(&self) -> StageResult {
|
||||||
|
let swap_size: KiraSize = self.get_swap_size();
|
||||||
|
let dev_name = self
|
||||||
|
.device
|
||||||
|
.clone()
|
||||||
|
.expect("Device should be selected!")
|
||||||
|
.name;
|
||||||
StageResult::new("partition")
|
StageResult::new("partition")
|
||||||
.add_val_string(
|
.add_val_string("device", dev_name)
|
||||||
"device",
|
|
||||||
self.device
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|v| Some(v.name.clone()))
|
|
||||||
.unwrap_or_else(|| "NONE".to_string()),
|
|
||||||
)
|
|
||||||
.add_val_string(
|
.add_val_string(
|
||||||
"swap_mode",
|
"swap_mode",
|
||||||
self.swap_mode
|
self.swap_mode
|
||||||
@@ -220,8 +235,10 @@ impl PartitionStage {
|
|||||||
.unwrap_or(SwapMode::NoSwap)
|
.unwrap_or(SwapMode::NoSwap)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
)
|
||||||
.add_val_bool("zram", self.use_zram)
|
.add_val_u64("swap_size_mb", swap_size.mb())
|
||||||
|
.add_val_bool("use_zram", self.use_zram)
|
||||||
.add_val_bool("secure_boot", self.secure_boot)
|
.add_val_bool("secure_boot", self.secure_boot)
|
||||||
|
.add_val_bool("encrypt_drive", self.encrypt_drive)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> StageAction {
|
pub fn update(&mut self, message: Message) -> StageAction {
|
||||||
@@ -244,7 +261,7 @@ impl PartitionStage {
|
|||||||
let swap_size: KiraSize = self.get_swap_size();
|
let swap_size: KiraSize = self.get_swap_size();
|
||||||
|
|
||||||
self.generated_disk_parts = Some(
|
self.generated_disk_parts = Some(
|
||||||
PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(512), swap_size)
|
PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(128), swap_size)
|
||||||
.part_list
|
.part_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|part_info| part_to_ct_params(part_info, dev.size))
|
.map(|part_info| part_to_ct_params(part_info, dev.size))
|
||||||
@@ -253,22 +270,25 @@ impl PartitionStage {
|
|||||||
|
|
||||||
self.device = Some(dev);
|
self.device = Some(dev);
|
||||||
StageAction::None
|
StageAction::None
|
||||||
}
|
},
|
||||||
|
|
||||||
Message::SelectSwapMode(sm) => {
|
Message::SelectSwapMode(sm) => {
|
||||||
self.swap_mode = Some(sm);
|
self.swap_mode = Some(sm);
|
||||||
self.gen_layout();
|
self.gen_layout();
|
||||||
StageAction::None
|
StageAction::None
|
||||||
}
|
},
|
||||||
Message::ToggleSecureBoot(t) => {
|
Message::ToggleSecureBoot(t) => {
|
||||||
self.secure_boot = t;
|
self.secure_boot = t;
|
||||||
StageAction::None
|
StageAction::None
|
||||||
}
|
},
|
||||||
|
Message::ToggleEncrypDrive(t) => {
|
||||||
|
self.encrypt_drive = t;
|
||||||
|
StageAction::None
|
||||||
|
},
|
||||||
Message::ToggleZram(t) => {
|
Message::ToggleZram(t) => {
|
||||||
self.use_zram = t;
|
self.use_zram = t;
|
||||||
self.gen_layout();
|
self.gen_layout();
|
||||||
StageAction::None
|
StageAction::None
|
||||||
}
|
},
|
||||||
Message::SwapSizeIncrement => {
|
Message::SwapSizeIncrement => {
|
||||||
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
|
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
|
||||||
.strict_mul(1024u32)
|
.strict_mul(1024u32)
|
||||||
@@ -276,7 +296,7 @@ impl PartitionStage {
|
|||||||
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
|
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
|
||||||
self.gen_layout();
|
self.gen_layout();
|
||||||
StageAction::None
|
StageAction::None
|
||||||
}
|
},
|
||||||
Message::SwapSizeDecrement => {
|
Message::SwapSizeDecrement => {
|
||||||
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
|
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
|
||||||
.strict_mul(1024u32)
|
.strict_mul(1024u32)
|
||||||
@@ -284,7 +304,7 @@ impl PartitionStage {
|
|||||||
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
|
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
|
||||||
self.gen_layout();
|
self.gen_layout();
|
||||||
StageAction::None
|
StageAction::None
|
||||||
}
|
},
|
||||||
Message::SwapSizeFieldChange(f_v) => {
|
Message::SwapSizeFieldChange(f_v) => {
|
||||||
let mut only_num = f_v.trim().to_string();
|
let mut only_num = f_v.trim().to_string();
|
||||||
only_num.retain(char::is_numeric);
|
only_num.retain(char::is_numeric);
|
||||||
@@ -292,7 +312,7 @@ impl PartitionStage {
|
|||||||
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
|
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
|
||||||
self.gen_layout();
|
self.gen_layout();
|
||||||
StageAction::None
|
StageAction::None
|
||||||
}
|
},
|
||||||
Message::Back => StageAction::Back,
|
Message::Back => StageAction::Back,
|
||||||
Message::Next => StageAction::Next(self.gen_result()),
|
Message::Next => StageAction::Next(self.gen_result()),
|
||||||
}
|
}
|
||||||
@@ -320,7 +340,11 @@ impl PartitionStage {
|
|||||||
let dev_parts_content = widget::column![dev_parts_text, dev_parts_content];
|
let dev_parts_content = widget::column![dev_parts_text, dev_parts_content];
|
||||||
|
|
||||||
let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back);
|
let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back);
|
||||||
let next_button = widget::button(widget::text(t!("button.next"))).on_press(Message::Next);
|
let next_button = if self.device.is_some() {
|
||||||
|
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
|
||||||
|
} else {
|
||||||
|
widget::button(widget::text(t!("button.next")))
|
||||||
|
};
|
||||||
|
|
||||||
let selected_dev_text = match self.selected_disk_parts {
|
let selected_dev_text = match self.selected_disk_parts {
|
||||||
Some(_) => widget::text(t!("partition.current_dev_layout")),
|
Some(_) => widget::text(t!("partition.current_dev_layout")),
|
||||||
@@ -329,6 +353,12 @@ impl PartitionStage {
|
|||||||
let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 64);
|
let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 64);
|
||||||
let selected_dev_content = widget::column![selected_dev_text, selected_dev_content];
|
let selected_dev_content = widget::column![selected_dev_text, selected_dev_content];
|
||||||
|
|
||||||
|
let use_sec_boot_checkbox = if self.secure_boot_in_setup_mode {
|
||||||
|
widget::checkbox(self.secure_boot).label(t!("partition.use_secure_boot"))
|
||||||
|
.on_toggle(Message::ToggleSecureBoot)
|
||||||
|
}
|
||||||
|
else {widget::checkbox(self.secure_boot).label(t!("partition.use_secure_boot"))};
|
||||||
|
|
||||||
widget::column![
|
widget::column![
|
||||||
widget::container(
|
widget::container(
|
||||||
widget::column![
|
widget::column![
|
||||||
@@ -354,9 +384,11 @@ impl PartitionStage {
|
|||||||
.label(t!("partition.use_zram"))
|
.label(t!("partition.use_zram"))
|
||||||
.on_toggle(Message::ToggleZram),
|
.on_toggle(Message::ToggleZram),
|
||||||
widget::rule::horizontal(2),
|
widget::rule::horizontal(2),
|
||||||
widget::checkbox(self.secure_boot)
|
use_sec_boot_checkbox,
|
||||||
.label(t!("partition.use_secure_boot"))
|
widget::rule::horizontal(2),
|
||||||
.on_toggle(Message::ToggleSecureBoot),
|
widget::checkbox(self.encrypt_drive)
|
||||||
|
.label(t!("partition.encrypt_drive"))
|
||||||
|
.on_toggle(Message::ToggleEncrypDrive),
|
||||||
widget::rule::horizontal(2),
|
widget::rule::horizontal(2),
|
||||||
selected_dev_content,
|
selected_dev_content,
|
||||||
dev_parts_content,
|
dev_parts_content,
|
||||||
|
|||||||
@@ -0,0 +1,290 @@
|
|||||||
|
// <Kira Installer - universal Linux installer.>
|
||||||
|
// Copyright (C) <2026> <Kira Foundation>
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is Security stage, setup users logins and such
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::stage::{StageAction, StageResult};
|
||||||
|
use iced::{Alignment, Color, widget};
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
enum PasswordStrenght {
|
||||||
|
Low,
|
||||||
|
Middle,
|
||||||
|
Hight,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PasswordStrenght {
|
||||||
|
pub fn from_password(password: &str) -> Self {
|
||||||
|
if password.len() < 8 {
|
||||||
|
Self::Low
|
||||||
|
} else if password.len() < 16 {
|
||||||
|
Self::Middle
|
||||||
|
} else {
|
||||||
|
Self::Hight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn color(&self) -> Color {
|
||||||
|
match self {
|
||||||
|
Self::Low => Color::from_rgb8(212, 32, 32),
|
||||||
|
Self::Middle => Color::from_rgb8(212, 212, 32),
|
||||||
|
Self::Hight => Color::from_rgb8(32, 212, 32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for PasswordStrenght {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Low => f.write_str(t!("security.pass_low").as_ref()),
|
||||||
|
Self::Middle => f.write_str(t!("security.pass_middle").as_ref()),
|
||||||
|
Self::Hight => f.write_str(t!("security.pass_hight").as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SecurityStage {
|
||||||
|
user_name: String,
|
||||||
|
user_full_name: String,
|
||||||
|
user_password: String,
|
||||||
|
root_password: String,
|
||||||
|
reuse_user_password_for_roor: bool,
|
||||||
|
user_pass_is_secure: bool,
|
||||||
|
root_pass_is_secure: bool,
|
||||||
|
user_password_strenght: PasswordStrenght,
|
||||||
|
root_password_strenght: PasswordStrenght,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Message {
|
||||||
|
ToggleReusePass(bool),
|
||||||
|
UserNameChange(String),
|
||||||
|
UserFullNameChange(String),
|
||||||
|
UserPasswordChange(String),
|
||||||
|
RootPasswordChange(String),
|
||||||
|
ToggleUserPassSecure,
|
||||||
|
ToggleRootPassSecure,
|
||||||
|
Next,
|
||||||
|
Back,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mothod safely retain first n chars in utf-8 string avoiding cutting utf-8 char in the middle
|
||||||
|
fn retain_first_n(s: &str, n: usize) -> &str {
|
||||||
|
s.char_indices()
|
||||||
|
.nth(n)
|
||||||
|
.and_then(|(idx, _)| Some(&s[..idx]))
|
||||||
|
.unwrap_or(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecurityStage {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
user_name: "".into(),
|
||||||
|
user_full_name: "".into(),
|
||||||
|
user_password: "".into(),
|
||||||
|
root_password: "".into(),
|
||||||
|
reuse_user_password_for_roor: false,
|
||||||
|
user_pass_is_secure: true,
|
||||||
|
root_pass_is_secure: true,
|
||||||
|
user_password_strenght: PasswordStrenght::Low,
|
||||||
|
root_password_strenght: PasswordStrenght::Low,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_result(&self) -> StageResult {
|
||||||
|
StageResult::new("security")
|
||||||
|
.add_val_string("user_name", self.user_name.clone())
|
||||||
|
.add_val_string("user_full_name", self.user_full_name.clone())
|
||||||
|
.add_val_string("user_full_name", self.user_full_name.clone())
|
||||||
|
.add_val_string("user_password", self.user_password.clone())
|
||||||
|
.add_val_string("root_password", self.root_password.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, message: Message) -> StageAction {
|
||||||
|
match message {
|
||||||
|
Message::ToggleReusePass(reuse_pass) => {
|
||||||
|
self.reuse_user_password_for_roor = reuse_pass;
|
||||||
|
if reuse_pass {
|
||||||
|
self.root_password = self.user_password.clone();
|
||||||
|
}
|
||||||
|
StageAction::None
|
||||||
|
}
|
||||||
|
Message::UserNameChange(u_name) => {
|
||||||
|
let mut trimmed = u_name
|
||||||
|
.trim_start_matches(|c: char| !c.is_ascii_alphabetic())
|
||||||
|
.to_string();
|
||||||
|
trimmed.retain(|c| c.is_ascii_alphanumeric() || c == '-');
|
||||||
|
trimmed.truncate(30); // it is safe bacuse we retained only ascii characters
|
||||||
|
self.user_name = trimmed;
|
||||||
|
StageAction::None
|
||||||
|
}
|
||||||
|
Message::UserFullNameChange(f_name) => {
|
||||||
|
let mut res = f_name.clone();
|
||||||
|
res.retain(|c| c.is_alphanumeric() || ['_', '-', ' ', '.', ','].contains(&c));
|
||||||
|
self.user_full_name = retain_first_n(&res, 128).to_string();
|
||||||
|
StageAction::None
|
||||||
|
}
|
||||||
|
Message::UserPasswordChange(u_pass) => {
|
||||||
|
let mut res = u_pass;
|
||||||
|
res.retain(|c: char| !c.is_control());
|
||||||
|
self.user_password = res;
|
||||||
|
self.user_password_strenght = PasswordStrenght::from_password(&self.user_password);
|
||||||
|
if self.reuse_user_password_for_roor {
|
||||||
|
self.root_password_strenght = self.user_password_strenght;
|
||||||
|
self.root_password = self.user_password.clone();
|
||||||
|
}
|
||||||
|
StageAction::None
|
||||||
|
}
|
||||||
|
Message::RootPasswordChange(r_pass) => {
|
||||||
|
let mut res: String = r_pass;
|
||||||
|
res.retain(|c: char| !c.is_control());
|
||||||
|
self.root_password = res;
|
||||||
|
self.root_password_strenght = PasswordStrenght::from_password(&self.root_password);
|
||||||
|
StageAction::None
|
||||||
|
}
|
||||||
|
Message::ToggleUserPassSecure => {
|
||||||
|
self.user_pass_is_secure = !self.user_pass_is_secure;
|
||||||
|
StageAction::None
|
||||||
|
}
|
||||||
|
Message::ToggleRootPassSecure => {
|
||||||
|
self.root_pass_is_secure = !self.root_pass_is_secure;
|
||||||
|
StageAction::None
|
||||||
|
}
|
||||||
|
Message::Back => StageAction::Back,
|
||||||
|
Message::Next => StageAction::Next(self.gen_result()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view(&self) -> iced::Element<'_, Message> {
|
||||||
|
let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back);
|
||||||
|
let next_button = if self.user_name.is_empty()
|
||||||
|
|| self.user_password.is_empty()
|
||||||
|
|| self.root_password.is_empty()
|
||||||
|
{
|
||||||
|
widget::button(widget::text(t!("button.next")))
|
||||||
|
} else {
|
||||||
|
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_pass_sec_text = if self.user_pass_is_secure {
|
||||||
|
widget::text("👁")
|
||||||
|
} else {
|
||||||
|
widget::text("*")
|
||||||
|
};
|
||||||
|
let root_pass_sec_text = if self.root_pass_is_secure {
|
||||||
|
widget::text("👁")
|
||||||
|
} else {
|
||||||
|
widget::text("*")
|
||||||
|
};
|
||||||
|
let user_pass_sec_text = user_pass_sec_text
|
||||||
|
.width(12)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.align_y(Alignment::Center);
|
||||||
|
let root_pass_sec_text = root_pass_sec_text
|
||||||
|
.width(12)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.align_y(Alignment::Center);
|
||||||
|
let mut root_pass_input =
|
||||||
|
widget::text_input("", &self.root_password).secure(self.root_pass_is_secure);
|
||||||
|
if !self.reuse_user_password_for_roor {
|
||||||
|
root_pass_input = root_pass_input.on_input(Message::RootPasswordChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
let user_p_strenght_text = widget::text(self.user_password_strenght.to_string())
|
||||||
|
.color(self.user_password_strenght.color());
|
||||||
|
let root_p_strenght_text = widget::text(self.root_password_strenght.to_string())
|
||||||
|
.color(self.root_password_strenght.color());
|
||||||
|
|
||||||
|
widget::column![
|
||||||
|
widget::container(
|
||||||
|
widget::column![
|
||||||
|
widget::column![
|
||||||
|
widget::text(t!("security.user_name")),
|
||||||
|
widget::text_input("", &self.user_name)
|
||||||
|
.width(256)
|
||||||
|
.on_input(Message::UserNameChange)
|
||||||
|
],
|
||||||
|
widget::column![
|
||||||
|
widget::text(t!("security.user_full_name")),
|
||||||
|
widget::text_input("", &self.user_full_name)
|
||||||
|
.width(256)
|
||||||
|
.on_input(Message::UserFullNameChange)
|
||||||
|
],
|
||||||
|
widget::column![
|
||||||
|
widget::text(t!("security.user_password")),
|
||||||
|
widget::row![
|
||||||
|
widget::text_input("", &self.user_password)
|
||||||
|
.width(512)
|
||||||
|
.on_input(Message::UserPasswordChange)
|
||||||
|
.secure(self.user_pass_is_secure),
|
||||||
|
widget::button(user_pass_sec_text)
|
||||||
|
.on_press(Message::ToggleUserPassSecure),
|
||||||
|
widget::container(user_p_strenght_text)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.style(widget::container::bordered_box)
|
||||||
|
.style(widget::container::rounded_box)
|
||||||
|
.padding(6)
|
||||||
|
.width(160),
|
||||||
|
]
|
||||||
|
.spacing(5)
|
||||||
|
.align_y(Alignment::Center)
|
||||||
|
],
|
||||||
|
widget::rule::horizontal(2),
|
||||||
|
widget::column![
|
||||||
|
widget::text(t!("security.root_password")),
|
||||||
|
widget::row![
|
||||||
|
root_pass_input.width(512),
|
||||||
|
widget::button(root_pass_sec_text)
|
||||||
|
.on_press(Message::ToggleRootPassSecure),
|
||||||
|
widget::container(root_p_strenght_text)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.style(widget::container::rounded_box)
|
||||||
|
.style(widget::container::bordered_box)
|
||||||
|
.padding(6)
|
||||||
|
.width(160),
|
||||||
|
]
|
||||||
|
.spacing(5)
|
||||||
|
.align_y(Alignment::Center),
|
||||||
|
widget::checkbox(self.reuse_user_password_for_roor)
|
||||||
|
.label(t!("security.reuse_user_password"))
|
||||||
|
.on_toggle(Message::ToggleReusePass),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
.padding(10)
|
||||||
|
.spacing(10)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
)
|
||||||
|
.height(iced::Length::Fill)
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.align_y(Alignment::Center),
|
||||||
|
widget::row![
|
||||||
|
back_button,
|
||||||
|
widget::space::horizontal(), // Pushes the right button to the far right
|
||||||
|
next_button,
|
||||||
|
]
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.align_y(Alignment::End)
|
||||||
|
.padding(10),
|
||||||
|
]
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.spacing(10)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user