WIP
This commit is contained in:
parent
0df7ab21e5
commit
cf91b29724
8 changed files with 357 additions and 59 deletions
70
Cargo.lock
generated
70
Cargo.lock
generated
|
|
@ -43,9 +43,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.1.4"
|
version = "4.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
|
@ -58,9 +58,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.1.0"
|
version = "4.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
|
|
@ -71,9 +71,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
|
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
@ -89,6 +89,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
@ -131,9 +137,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01"
|
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
|
|
@ -143,9 +149,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.5"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
|
|
@ -153,9 +159,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
|
checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"io-lifetimes",
|
"io-lifetimes",
|
||||||
|
|
@ -164,10 +170,19 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itertools"
|
||||||
version = "1.0.5"
|
version = "0.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
|
|
@ -230,9 +245,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.17.0"
|
version = "1.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
|
|
@ -313,9 +328,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.36.8"
|
version = "0.36.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
|
|
@ -327,9 +342,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.12"
|
version = "1.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
|
|
@ -353,9 +368,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.92"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a"
|
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
|
@ -376,9 +391,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.107"
|
version = "1.0.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -393,6 +408,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"dbus",
|
"dbus",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"nix",
|
"nix",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -410,9 +426,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.6"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ anyhow = "1.0.68"
|
||||||
clap = { version = "4.1.4", features = ["derive"] }
|
clap = { version = "4.1.4", features = ["derive"] }
|
||||||
dbus = "0.9.7"
|
dbus = "0.9.7"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
|
itertools = "0.10.5"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
nix = "0.26.2"
|
nix = "0.26.2"
|
||||||
serde = { version = "1.0.152", features = ["derive"] }
|
serde = { version = "1.0.152", features = ["derive"] }
|
||||||
|
|
|
||||||
11
nix/lib.nix
11
nix/lib.nix
|
|
@ -104,15 +104,12 @@ in
|
||||||
${system-manager}/bin/system-manager deactivate "$@"
|
${system-manager}/bin/system-manager deactivate "$@"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
linkFarmEntryFromDrv = drv: {
|
linkFarmNestedEntryFromDrv = dirs: drv: {
|
||||||
name = drv.name;
|
name = lib.concatStringsSep "/" (dirs ++ [ "${drv.name}" ]);
|
||||||
path = drv;
|
|
||||||
};
|
|
||||||
|
|
||||||
linkFarmBinEntryFromDrv = drv: {
|
|
||||||
name = "bin/${drv.name}";
|
|
||||||
path = drv;
|
path = drv;
|
||||||
};
|
};
|
||||||
|
linkFarmEntryFromDrv = linkFarmNestedEntryFromDrv [ ];
|
||||||
|
linkFarmBinEntryFromDrv = linkFarmNestedEntryFromDrv [ "bin" ];
|
||||||
in
|
in
|
||||||
returnIfNoAssertions (
|
returnIfNoAssertions (
|
||||||
pkgs.linkFarm "system-manager" [
|
pkgs.linkFarm "system-manager" [
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,20 @@ let
|
||||||
group = "root";
|
group = "root";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"a/nested/example/foo3" = {
|
||||||
|
text = "boo!";
|
||||||
|
mode = "0764";
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
};
|
||||||
|
|
||||||
|
"a/nested/example2/foo3" = {
|
||||||
|
text = "boo!";
|
||||||
|
mode = "0764";
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
};
|
||||||
|
|
||||||
out-of-store = {
|
out-of-store = {
|
||||||
source = "/run/systemd/system/";
|
source = "/run/systemd/system/";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
|
||||||
log::info!("Running in ephemeral mode");
|
log::info!("Running in ephemeral mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO we probably need to first deactivate left-over files and services
|
||||||
|
// before we start putting in place the new ones.
|
||||||
etc_files::activate(store_path, ephemeral)?;
|
etc_files::activate(store_path, ephemeral)?;
|
||||||
services::activate(store_path, ephemeral)?;
|
services::activate(store_path, ephemeral)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::DirBuilder;
|
use std::fs::{DirBuilder, Permissions};
|
||||||
|
use std::os::unix::prelude::PermissionsExt;
|
||||||
use std::path;
|
use std::path;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
|
||||||
use crate::{create_link, create_store_link, StorePath};
|
use crate::{
|
||||||
|
create_link, create_store_link, remove_dir, remove_file, remove_link, StorePath,
|
||||||
|
ETC_STATE_FILE_NAME, SYSTEM_MANAGER_STATE_DIR,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct EtcFile {
|
struct EtcFile {
|
||||||
source: StorePath,
|
source: StorePath,
|
||||||
|
|
@ -22,13 +27,77 @@ struct EtcFile {
|
||||||
|
|
||||||
type EtcFiles = HashMap<String, EtcFile>;
|
type EtcFiles = HashMap<String, EtcFile>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct EtcFilesConfig {
|
struct EtcFilesConfig {
|
||||||
entries: EtcFiles,
|
entries: EtcFiles,
|
||||||
static_env: StorePath,
|
static_env: StorePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct EtcStateInfo {
|
||||||
|
status: EtcFileStatus,
|
||||||
|
// TODO directories and files are now both represented as a string associated with a nested
|
||||||
|
// map. For files the nested map is simple empty.
|
||||||
|
// We could potentially optimise this.
|
||||||
|
nested: HashMap<String, EtcStateInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
enum EtcFileStatus {
|
||||||
|
Managed,
|
||||||
|
Unmanaged,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EtcFileStatus {
|
||||||
|
fn merge(&self, other: &Self) -> Self {
|
||||||
|
use EtcFileStatus::*;
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(Unmanaged, Unmanaged) => Unmanaged,
|
||||||
|
_ => Managed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EtcStateInfo {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::with_status(EtcFileStatus::Unmanaged)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_status(status: EtcFileStatus) -> Self {
|
||||||
|
Self {
|
||||||
|
status,
|
||||||
|
nested: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO unit tests
|
||||||
|
fn register_managed_path(mut self, components: &[String], path: String) -> Self {
|
||||||
|
let state = components.iter().fold(&mut self, |state, component| {
|
||||||
|
if !state.nested.contains_key(component) {
|
||||||
|
let new_state = Self::new();
|
||||||
|
state.nested.insert(component.to_owned(), new_state);
|
||||||
|
}
|
||||||
|
state.nested.get_mut(component).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
state
|
||||||
|
.nested
|
||||||
|
.insert(path, Self::with_status(EtcFileStatus::Managed));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct CreatedEtcFile {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
|
pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
|
||||||
log::info!("Reading etc file definitions...");
|
log::info!("Reading etc file definitions...");
|
||||||
let file = fs::File::open(Path::new(&store_path.store_path).join("etcFiles/etcFiles.json"))?;
|
let file = fs::File::open(Path::new(&store_path.store_path).join("etcFiles/etcFiles.json"))?;
|
||||||
|
|
@ -40,47 +109,204 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
|
||||||
log::debug!("Storing /etc entries in {}", etc_dir.display());
|
log::debug!("Storing /etc entries in {}", etc_dir.display());
|
||||||
|
|
||||||
DirBuilder::new().recursive(true).create(&etc_dir)?;
|
DirBuilder::new().recursive(true).create(&etc_dir)?;
|
||||||
create_store_link(
|
|
||||||
&config.static_env,
|
|
||||||
etc_dir.join(".system-manager-static").as_path(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
config
|
// TODO: constant?
|
||||||
.entries
|
let static_dir_name = ".system-manager-static";
|
||||||
.into_iter()
|
let static_path = etc_dir.join(static_dir_name);
|
||||||
.try_for_each(|(name, entry)| create_etc_link(&name, &entry, &etc_dir))?;
|
create_store_link(&config.static_env, static_path.as_path())?;
|
||||||
|
|
||||||
// TODO register the crated files (including .system-manager-static) in a
|
// TODO remove all paths that are in the state file from the previous generation
|
||||||
// state file, and clean up old files from the previous generation
|
// and not in the current one.
|
||||||
|
|
||||||
|
let state = read_created_files()?;
|
||||||
|
let new_state = create_etc_links(config.entries.values(), &etc_dir, state);
|
||||||
|
serialise_created_files(&new_state.register_managed_path(&[], static_dir_name.to_owned()))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO we need to record in a state file which files we created and then remove them
|
|
||||||
pub fn deactivate() -> Result<()> {
|
pub fn deactivate() -> Result<()> {
|
||||||
|
let old_created_files = read_created_files()?;
|
||||||
|
log::debug!("{:?}", old_created_files);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//old_created_files
|
||||||
|
// .iter()
|
||||||
|
// .try_for_each(|created_file| remove_created_file(&created_file.path, "etc"))?;
|
||||||
|
|
||||||
|
serialise_created_files(&EtcStateInfo::new())?;
|
||||||
|
|
||||||
|
log::info!("Done");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_etc_link(name: &str, entry: &EtcFile, etc_dir: &Path) -> Result<()> {
|
fn remove_created_file<P>(created_file: &P, root_dir: &str) -> Result<()>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let path = created_file.as_ref();
|
||||||
|
let recurse = if path.is_file() {
|
||||||
|
remove_file(path)?;
|
||||||
|
true
|
||||||
|
} else if path.is_symlink() {
|
||||||
|
remove_link(path)?;
|
||||||
|
true
|
||||||
|
} else if path.is_dir() && fs::read_dir(created_file)?.next().is_none() {
|
||||||
|
log::info!("We will remove the following directory: {}", path.display());
|
||||||
|
remove_dir(path)?;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
log::debug!("Stopped at directory {}.", path.display());
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if recurse {
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
if parent
|
||||||
|
.file_name()
|
||||||
|
.filter(|name| &root_dir != name)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
log::debug!("Recursing up into directory {}...", parent.display());
|
||||||
|
return remove_created_file(&parent, root_dir);
|
||||||
|
}
|
||||||
|
log::debug!("Reached root dir: {}", parent.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_etc_links<'a, E>(entries: E, etc_dir: &Path, state: EtcStateInfo) -> EtcStateInfo
|
||||||
|
where
|
||||||
|
E: Iterator<Item = &'a EtcFile>,
|
||||||
|
{
|
||||||
|
entries.fold(state, |state, entry| {
|
||||||
|
let (new_state, status) = create_etc_link(entry, etc_dir, state);
|
||||||
|
match status {
|
||||||
|
Ok(_) => new_state,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Error while creating file in {}: {e}", etc_dir.display());
|
||||||
|
new_state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_etc_link(
|
||||||
|
entry: &EtcFile,
|
||||||
|
etc_dir: &Path,
|
||||||
|
state: EtcStateInfo,
|
||||||
|
) -> (EtcStateInfo, Result<()>) {
|
||||||
if entry.mode == "symlink" {
|
if entry.mode == "symlink" {
|
||||||
if let Some(path::Component::Normal(link_target)) =
|
if let Some(path::Component::Normal(link_target)) =
|
||||||
entry.target.components().into_iter().next()
|
entry.target.components().into_iter().next()
|
||||||
{
|
{
|
||||||
create_link(
|
let link_name = etc_dir.join(link_target);
|
||||||
|
match create_link(
|
||||||
Path::new(".")
|
Path::new(".")
|
||||||
.join(".system-manager-static")
|
.join(".system-manager-static")
|
||||||
.join("etc")
|
.join("etc")
|
||||||
.join(link_target)
|
.join(link_target)
|
||||||
.as_path(),
|
.as_path(),
|
||||||
etc_dir.join(link_target).as_path(),
|
link_name.as_path(),
|
||||||
|
) {
|
||||||
|
Ok(_) => (
|
||||||
|
state.register_managed_path(&[], link_target.to_string_lossy().into_owned()),
|
||||||
|
Ok(()),
|
||||||
|
),
|
||||||
|
e => (state, e),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
Err(anyhow!("Cannot create link: {}", entry.target.display(),)),
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
anyhow::bail!("Cannot create link for this entry ({name}).")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let dirbuilder = DirBuilder::new();
|
||||||
|
let target_path = etc_dir.join(entry.target.as_path());
|
||||||
|
|
||||||
|
// Create all parent dirs that do not exist yet
|
||||||
|
let (new_state, created_paths, _, status) = target_path
|
||||||
|
.parent()
|
||||||
|
.unwrap() // TODO
|
||||||
|
.components()
|
||||||
|
.fold_while(
|
||||||
|
(state, Vec::new(), PathBuf::from("/"), Ok(())),
|
||||||
|
|(state, mut created, path, _), component| {
|
||||||
|
use itertools::FoldWhile::{Continue, Done};
|
||||||
|
use path::Component;
|
||||||
|
|
||||||
|
match component {
|
||||||
|
Component::RootDir => Continue((state, created, path, Ok(()))),
|
||||||
|
Component::Normal(dir) => {
|
||||||
|
let new_path = path.join(dir);
|
||||||
|
if !new_path.exists() {
|
||||||
|
log::debug!("Creating path: {}", new_path.display());
|
||||||
|
match dirbuilder.create(new_path.as_path()) {
|
||||||
|
Ok(_) => {
|
||||||
|
let new_state = state.register_managed_path(
|
||||||
|
&created,
|
||||||
|
dir.to_string_lossy().into_owned(),
|
||||||
|
);
|
||||||
|
created.push(dir.to_string_lossy().into_owned());
|
||||||
|
Continue((new_state, created, new_path, Ok(())))
|
||||||
|
}
|
||||||
|
Err(e) => Done((state, created, path, Err(anyhow!(e)))),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
created.push(dir.to_string_lossy().into_owned());
|
||||||
|
Continue((state, created, new_path, Ok(())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
otherwise => Done((
|
||||||
|
state,
|
||||||
|
created,
|
||||||
|
path,
|
||||||
|
Err(anyhow!(
|
||||||
|
"Unexpected path component encountered: {:?}",
|
||||||
|
otherwise
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
match status.and_then(|_| {
|
||||||
|
copy_file(
|
||||||
|
entry
|
||||||
|
.source
|
||||||
|
.store_path
|
||||||
|
.join("etc")
|
||||||
|
.join(&entry.target)
|
||||||
|
.as_path(),
|
||||||
|
&target_path,
|
||||||
|
&entry.mode,
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
Ok(_) => (
|
||||||
|
new_state.register_managed_path(
|
||||||
|
&created_paths,
|
||||||
|
target_path
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
),
|
||||||
|
Ok(()),
|
||||||
|
),
|
||||||
|
e => (new_state, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_file(source: &Path, target: &Path, mode: &str) -> Result<()> {
|
||||||
|
fs::copy(source, target)?;
|
||||||
|
let mode_int = u32::from_str_radix(mode, 8).map_err(anyhow::Error::from)?;
|
||||||
|
fs::set_permissions(target, Permissions::from_mode(mode_int))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn etc_dir(ephemeral: bool) -> PathBuf {
|
fn etc_dir(ephemeral: bool) -> PathBuf {
|
||||||
if ephemeral {
|
if ephemeral {
|
||||||
|
|
@ -89,3 +315,35 @@ fn etc_dir(ephemeral: bool) -> PathBuf {
|
||||||
Path::new("/etc").to_path_buf()
|
Path::new("/etc").to_path_buf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialise_created_files(created_files: &EtcStateInfo) -> Result<()> {
|
||||||
|
let state_file = Path::new(SYSTEM_MANAGER_STATE_DIR).join(ETC_STATE_FILE_NAME);
|
||||||
|
DirBuilder::new()
|
||||||
|
.recursive(true)
|
||||||
|
.create(SYSTEM_MANAGER_STATE_DIR)?;
|
||||||
|
|
||||||
|
log::info!("Writing state info into file: {}", state_file.display());
|
||||||
|
let writer = io::BufWriter::new(fs::File::create(state_file)?);
|
||||||
|
serde_json::to_writer(writer, created_files)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_created_files() -> Result<EtcStateInfo> {
|
||||||
|
let state_file = Path::new(SYSTEM_MANAGER_STATE_DIR).join(ETC_STATE_FILE_NAME);
|
||||||
|
DirBuilder::new()
|
||||||
|
.recursive(true)
|
||||||
|
.create(SYSTEM_MANAGER_STATE_DIR)?;
|
||||||
|
|
||||||
|
if Path::new(&state_file).is_file() {
|
||||||
|
log::info!("Reading state info from {}", state_file.display());
|
||||||
|
let reader = io::BufReader::new(fs::File::open(state_file)?);
|
||||||
|
match serde_json::from_reader(reader) {
|
||||||
|
Ok(created_files) => return Ok(created_files),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Error reading the state file, ignoring.");
|
||||||
|
log::error!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(EtcStateInfo::new())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,6 @@ pub fn deactivate() -> Result<()> {
|
||||||
let old_linked_services = read_linked_services()?;
|
let old_linked_services = read_linked_services()?;
|
||||||
log::debug!("{:?}", old_linked_services);
|
log::debug!("{:?}", old_linked_services);
|
||||||
|
|
||||||
serialise_linked_services(&HashMap::new())?;
|
|
||||||
|
|
||||||
let service_manager = systemd::ServiceManager::new_session()?;
|
let service_manager = systemd::ServiceManager::new_session()?;
|
||||||
let timeout = Some(Duration::from_secs(30));
|
let timeout = Some(Duration::from_secs(30));
|
||||||
|
|
||||||
|
|
@ -102,6 +100,8 @@ pub fn deactivate() -> Result<()> {
|
||||||
log::info!("Reloading the systemd daemon...");
|
log::info!("Reloading the systemd daemon...");
|
||||||
service_manager.daemon_reload()?;
|
service_manager.daemon_reload()?;
|
||||||
|
|
||||||
|
serialise_linked_services(&HashMap::new())?;
|
||||||
|
|
||||||
log::info!("Done");
|
log::info!("Done");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/lib.rs
12
src/lib.rs
|
|
@ -14,7 +14,7 @@ const PROFILE_NAME: &str = "system-manager";
|
||||||
const GCROOT_PATH: &str = "/nix/var/nix/gcroots/system-manager-current";
|
const GCROOT_PATH: &str = "/nix/var/nix/gcroots/system-manager-current";
|
||||||
const SYSTEM_MANAGER_STATE_DIR: &str = "/var/lib/system-manager/state";
|
const SYSTEM_MANAGER_STATE_DIR: &str = "/var/lib/system-manager/state";
|
||||||
const SERVICES_STATE_FILE_NAME: &str = "services.json";
|
const SERVICES_STATE_FILE_NAME: &str = "services.json";
|
||||||
//const ETC_STATE_FILE_NAME: &str = "etc-files.json";
|
const ETC_STATE_FILE_NAME: &str = "etc-files.json";
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(from = "String", into = "String", rename_all = "camelCase")]
|
#[serde(from = "String", into = "String", rename_all = "camelCase")]
|
||||||
|
|
@ -77,6 +77,16 @@ fn remove_link(from: &Path) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_file(from: &Path) -> Result<()> {
|
||||||
|
log::info!("Removing file: {}", from.display());
|
||||||
|
fs::remove_file(from).map_err(anyhow::Error::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir(from: &Path) -> Result<()> {
|
||||||
|
log::info!("Removing directory: {}", from.display());
|
||||||
|
fs::remove_dir(from).map_err(anyhow::Error::from)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
|
pub fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
|
||||||
where
|
where
|
||||||
F: Fn(B) -> C,
|
F: Fn(B) -> C,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue