aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorsodiboo <37938646+sodiboo@users.noreply.github.com>2025-08-05 15:27:28 +0200
committerGitHub <noreply@github.com>2025-08-05 06:27:28 -0700
commit52c579d5567d0b16ae68177fad05c612baba38af (patch)
treea121c14ea4f379f70acf319153642e55d8f9237d /src/main.rs
parent5edd91d37b25a751880d3a7bd3b92f0016d0cdc4 (diff)
downloadniri-52c579d5567d0b16ae68177fad05c612baba38af.tar.gz
niri-52c579d5567d0b16ae68177fad05c612baba38af.tar.bz2
niri-52c579d5567d0b16ae68177fad05c612baba38af.zip
fix hot reloading `/etc/niri/config.kdl` (#1907)
* refactor config load logic, and properly watch the system config path * move config creation to niri-config, and make the errors a bit nicer notably, "error creating config" is now a cause for "error loading config", instead of it being one error and then "error loading config: no such file or directory". also, failure to load a config is now printed as an error level diagnostic (because it is indeed an error, not just a warning you can shrug off) * refactor watcher tests; add some new ones now they check for the file contents too! and i added some tests for ConfigPath::Regular, including a messy one with many symlink swaps * fixes --------- Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs82
1 files changed, 19 insertions, 63 deletions
diff --git a/src/main.rs b/src/main.rs
index 43325a77..007dd8b8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,10 +2,10 @@
extern crate tracing;
use std::fmt::Write as _;
-use std::fs::{self, File};
+use std::fs::File;
use std::io::{self, Write};
use std::os::fd::FromRawFd;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use std::process::Command;
use std::{env, mem};
@@ -25,7 +25,7 @@ use niri::utils::spawning::{
};
use niri::utils::watcher::Watcher;
use niri::utils::{cause_panic, version, xwayland, IS_SYSTEMD_SERVICE};
-use niri_config::Config;
+use niri_config::ConfigPath;
use niri_ipc::socket::SOCKET_PATH_ENV;
use portable_atomic::Ordering;
use sd_notify::NotifyState;
@@ -99,8 +99,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Sub::Validate { config } => {
tracy_client::Client::start();
- let (path, _, _) = config_path(config);
- Config::load(&path)?;
+ config_path(config).load()?;
info!("config is valid");
return Ok(());
}
@@ -144,47 +143,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
info!("starting version {}", &version());
// Load the config.
- let mut config_created = false;
- let (path, watch_path, create_default) = config_path(cli.config);
+ let config_path = config_path(cli.config);
env::remove_var("NIRI_CONFIG");
- if create_default {
- let default_parent = path.parent().unwrap();
-
- match fs::create_dir_all(default_parent) {
- Ok(()) => {
- // Create the config and fill it with the default config if it doesn't exist.
- let new_file = File::options()
- .read(true)
- .write(true)
- .create_new(true)
- .open(&path);
- match new_file {
- Ok(mut new_file) => {
- let default = include_bytes!("../resources/default-config.kdl");
- match new_file.write_all(default) {
- Ok(()) => {
- config_created = true;
- info!("wrote default config to {:?}", &path);
- }
- Err(err) => {
- warn!("error writing config file at {:?}: {err:?}", &path)
- }
- }
- }
- Err(err) if err.kind() == io::ErrorKind::AlreadyExists => {}
- Err(err) => warn!("error creating config file at {:?}: {err:?}", &path),
- }
- }
- Err(err) => {
- warn!(
- "error creating config directories {:?}: {err:?}",
- default_parent
- );
- }
- }
- }
-
- let config_load_result = Config::load(&path);
+ let (config_created_at, config_load_result) = config_path.load_or_create();
let config_errored = config_load_result.is_err();
let mut config = config_load_result
.map_err(|err| warn!("{err:?}"))
@@ -273,14 +234,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let _watcher = {
// Parsing the config actually takes > 20 ms on my beefy machine, so let's do it on the
// watcher thread.
- let process = |path: &Path| {
- Config::load(path).map_err(|err| {
- warn!("{:?}", err.context("error loading config"));
+ let process = |path: &ConfigPath| {
+ path.load().map_err(|err| {
+ warn!("{err:?}");
})
};
let (tx, rx) = calloop::channel::sync_channel(1);
- let watcher = Watcher::new(watch_path.clone(), process, tx);
+ let watcher = Watcher::new(config_path.clone(), process, tx);
event_loop
.handle()
.insert_source(rx, |event, _, state| match event {
@@ -301,7 +262,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Show the config error notification right away if needed.
if config_errored {
state.niri.config_error_notification.show();
- } else if config_created {
+ } else if let Some(path) = config_created_at {
state.niri.config_error_notification.show_created(path);
}
@@ -385,26 +346,21 @@ fn system_config_path() -> PathBuf {
PathBuf::from("/etc/niri/config.kdl")
}
-/// Resolves and returns the config path to load, the config path to watch, and whether to create
-/// the default config at the path to load.
-fn config_path(cli_path: Option<PathBuf>) -> (PathBuf, PathBuf, bool) {
+fn config_path(cli_path: Option<PathBuf>) -> ConfigPath {
if let Some(explicit) = cli_path.or_else(env_config_path) {
- return (explicit.clone(), explicit, false);
+ return ConfigPath::Explicit(explicit);
}
let system_path = system_config_path();
- if let Some(path) = default_config_path() {
- if path.exists() {
- return (path.clone(), path, false);
- }
- if system_path.exists() {
- (system_path, path, false)
- } else {
- (path.clone(), path, true)
+ if let Some(user_path) = default_config_path() {
+ ConfigPath::Regular {
+ user_path,
+ system_path,
}
} else {
- (system_path.clone(), system_path, false)
+ // Couldn't find the home directory, or whatever.
+ ConfigPath::Explicit(system_path)
}
}