Format-preserving TOML editing

Restore the automatic generation of a node_id if one isn't present,
based on a hash of /etc/machine_id. Switch from the "toml" crate
to the "toml_edit" crate, and use its Document type to preserve
the user's comments and formatting when editing the configuration
file for saving.
This commit is contained in:
Herbert Wolverson 2023-04-05 15:00:35 +00:00
parent fada0a6e92
commit a16bc66d79
4 changed files with 36 additions and 40 deletions

16
src/rust/Cargo.lock generated
View File

@ -812,7 +812,7 @@ dependencies = [
"atomic",
"pear",
"serde",
"toml 0.5.11",
"toml",
"uncased",
"version_check",
]
@ -1436,7 +1436,7 @@ dependencies = [
"serde_json",
"sha2",
"thiserror",
"toml 0.7.2",
"toml_edit",
"uuid",
]
@ -2787,18 +2787,6 @@ dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.1"

View File

@ -6,7 +6,7 @@ license = "GPL-2.0-only"
[dependencies]
thiserror = "1"
toml = "0"
toml_edit = { version = "0", features = [ "serde" ] }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1"
csv = "1"

View File

@ -68,7 +68,7 @@ impl WebUsers {
fn save_to_disk(&self) -> Result<(), AuthenticationError> {
let path = Self::path()?;
let new_contents = toml::to_string(&self);
let new_contents = toml_edit::ser::to_string(&self);
if let Err(e) = new_contents {
return Err(AuthenticationError::SerializationError(e));
}
@ -109,7 +109,7 @@ impl WebUsers {
} else {
// Load from disk
if let Ok(raw) = read_to_string(path) {
let parse_result = toml::from_str(&raw);
let parse_result = toml_edit::de::from_str(&raw);
if let Ok(users) = parse_result {
Ok(users)
} else {
@ -255,7 +255,7 @@ pub enum AuthenticationError {
#[error("Unable to load /etc/lqos.conf")]
UnableToLoadEtcLqos,
#[error("Unable to serialize to TOML")]
SerializationError(toml::ser::Error),
SerializationError(toml_edit::ser::Error),
#[error("Unable to remove existing web users file")]
UnableToDelete,
#[error("Unable to open lqusers.toml for writing. Check permissions?")]

View File

@ -1,6 +1,7 @@
//! Manages the `/etc/lqos.conf` file.
use log::error;
use serde::{Deserialize, Serialize};
use toml_edit::{Document, value};
use std::{fs, path::Path};
use thiserror::Error;
@ -136,17 +137,27 @@ impl EtcLqos {
return Err(EtcLqosError::ConfigDoesNotExist);
}
if let Ok(raw) = std::fs::read_to_string("/etc/lqos.conf") {
let config_result: Result<Self, toml::de::Error> = toml::from_str(&raw);
match config_result {
Ok(mut config) => {
check_config(&mut config);
Ok(config)
}
let document = raw.parse::<Document>();
match document {
Err(e) => {
error!("Unable to parse TOML from /etc/lqos.conf");
error!("Full error: {:?}", e);
Err(EtcLqosError::CannotParseToml)
}
Ok(mut config_doc) => {
let cfg = toml_edit::de::from_document::<EtcLqos>(config_doc.clone());
match cfg {
Ok(mut cfg) => {
check_config(&mut config_doc, &mut cfg);
Ok(cfg)
}
Err(e) => {
error!("Unable to parse TOML from /etc/lqos.conf");
error!("Full error: {:?}", e);
Err(EtcLqosError::CannotParseToml)
}
}
}
}
} else {
error!("Unable to read contents of /etc/lqos.conf");
@ -156,7 +167,7 @@ impl EtcLqos {
/// Saves changes made to /etc/lqos.conf
/// Copies current configuration into /etc/lqos.conf.backup first
pub fn save(&self) -> Result<(), EtcLqosError> {
pub fn save(&self, document: &mut Document) -> Result<(), EtcLqosError> {
let cfg_path = Path::new("/etc/lqos.conf");
let backup_path = Path::new("/etc/lqos.conf.backup");
if let Err(e) = std::fs::copy(cfg_path, backup_path) {
@ -164,26 +175,17 @@ impl EtcLqos {
log::error!("{e:?}");
return Err(EtcLqosError::BackupFail);
}
let new_cfg = toml::to_string_pretty(&self);
match new_cfg {
Err(e) => {
log::error!("Unable to serialize new /etc/lqos.conf");
log::error!("{e:?}");
return Err(EtcLqosError::SerializeFail);
}
Ok(new_cfg) => {
if let Err(e) = fs::write(cfg_path, new_cfg) {
log::error!("Unable to write to /etc/lqos.conf");
log::error!("{e:?}");
return Err(EtcLqosError::WriteFail);
}
}
let new_cfg = document.to_string();
if let Err(e) = fs::write(cfg_path, new_cfg) {
log::error!("Unable to write to /etc/lqos.conf");
log::error!("{e:?}");
return Err(EtcLqosError::WriteFail);
}
Ok(())
}
}
fn check_config(cfg: &mut EtcLqos) {
fn check_config(cfg_doc: &mut Document, cfg: &mut EtcLqos) {
use sha2::digest::Update;
use sha2::Digest;
@ -191,6 +193,12 @@ fn check_config(cfg: &mut EtcLqos) {
if let Ok(machine_id) = std::fs::read_to_string("/etc/machine-id") {
let hash = sha2::Sha256::new().chain(machine_id).finalize();
cfg.node_id = Some(format!("{:x}", hash));
cfg_doc["node_id"] = value(format!("{:x}", hash));
println!("Updating");
if let Err(e) = cfg.save(cfg_doc) {
log::error!("Unable to save /etc/lqos.conf");
log::error!("{e:?}");
}
}
}
}