fix!: create IPC socket in user runtime directory (#1313)
Each user has their own runtime directory at `/run/user/<uid>`. Creating the IPC socket in there makes sure it is cleaned up regardless of whether `ncspot` exits normally. BREAKING CHANGE: move IPC socket location
This commit is contained in:
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Improve error messages generated by the command line
|
||||
- Build with crossterm terminal backend by default
|
||||
- Move UNIX IPC socket from the user's cache path to the user's runtime directory
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
10
doc/users.md
10
doc/users.md
@@ -180,10 +180,12 @@ Note: \<FOO\> - mandatory arg; [BAR] - optional arg
|
||||
| `save [current]` | Save selected item, if `current` is passed the currently playing item will be saved |
|
||||
|
||||
## Remote control (IPC)
|
||||
Apart from MPRIS, ncspot will also create a domain socket on UNIX platforms
|
||||
(Linux, macOS, *BSD) at `~/.cache/ncspot/ncspot.sock`. Applications or scripts
|
||||
can connect to this socket to send commands or be notified of the currently
|
||||
playing track, i.e. with `netcat`:
|
||||
Apart from MPRIS, ncspot will also create a domain socket on UNIX platforms (Linux, macOS, *BSD).
|
||||
The socket will be created in the platform's runtime directory. If XDG_RUNTIME_DIR is set, it will
|
||||
be created under `$XDG_RUNTIME_DIR/ncspot`. If XDG_RUNTIME_DIR isn't set, it will be created under
|
||||
`/run/user/<uid>` for Linux if it exists. In all other cases, it will be created under
|
||||
`/tmp/ncspot-<uid>`. Applications or scripts can connect to this socket to send commands or be
|
||||
notified of the currently playing track, i.e. with `netcat`:
|
||||
|
||||
```
|
||||
% nc -U ~/.cache/ncspot/ncspot.sock
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::library::Library;
|
||||
use crate::queue::Queue;
|
||||
use crate::spotify::{PlayerEvent, Spotify};
|
||||
use crate::ui::create_cursive;
|
||||
use crate::{authentication, ui};
|
||||
use crate::{authentication, ui, utils};
|
||||
use crate::{command, queue, spotify};
|
||||
|
||||
#[cfg(feature = "mpris")]
|
||||
@@ -140,7 +140,9 @@ impl Application {
|
||||
#[cfg(unix)]
|
||||
let ipc = ipc::IpcSocket::new(
|
||||
ASYNC_RUNTIME.get().unwrap().handle(),
|
||||
crate::config::cache_path("ncspot.sock"),
|
||||
utils::create_runtime_directory()
|
||||
.unwrap()
|
||||
.join("ncspot.sock"),
|
||||
event_manager.clone(),
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
53
src/utils.rs
53
src/utils.rs
@@ -1,6 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::{fmt::Write, path::PathBuf};
|
||||
|
||||
/// Returns a human readable String of a Duration
|
||||
///
|
||||
@@ -59,3 +59,54 @@ pub fn download(url: String, path: std::path::PathBuf) -> Result<(), std::io::Er
|
||||
std::io::copy(&mut resp, &mut file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create the application specific runtime directory and return the path to it.
|
||||
///
|
||||
/// If the directory already exists and has the correct permissions, this function just returns the
|
||||
/// existing directory. The contents stored in this directory are not necessarily persisted across
|
||||
/// reboots. Stored files should be small since they could reside in memory (like on a tmpfs mount).
|
||||
#[cfg(unix)]
|
||||
pub fn create_runtime_directory() -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
use std::{
|
||||
fs::{self, Permissions},
|
||||
os::unix::prelude::PermissionsExt,
|
||||
};
|
||||
|
||||
let linux_runtime_directory =
|
||||
PathBuf::from(format!("/run/user/{}/", unsafe { libc::getuid() }));
|
||||
let unix_runtime_directory = PathBuf::from("/tmp/");
|
||||
|
||||
let user_runtime_directory = if let Some(xdg_runtime_directory) = xdg_runtime_directory() {
|
||||
Some(xdg_runtime_directory.join("ncspot"))
|
||||
} else if cfg!(linux) && linux_runtime_directory.exists() {
|
||||
Some(linux_runtime_directory.join("ncspot"))
|
||||
} else if unix_runtime_directory.exists() {
|
||||
Some(unix_runtime_directory.join(format!("ncspot-{}", unsafe { libc::getuid() })))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.ok_or("no runtime directory found")?;
|
||||
|
||||
let creation_result = fs::create_dir(&user_runtime_directory);
|
||||
|
||||
if creation_result.is_ok()
|
||||
|| matches!(
|
||||
creation_result.as_ref().unwrap_err().kind(),
|
||||
std::io::ErrorKind::AlreadyExists
|
||||
)
|
||||
{
|
||||
// Needed when created inside a world readable directory, to prevent unauthorized access.
|
||||
// Doesn't hurt otherwise.
|
||||
fs::set_permissions(&user_runtime_directory, Permissions::from_mode(0o700))?;
|
||||
|
||||
Ok(user_runtime_directory)
|
||||
} else {
|
||||
#[allow(clippy::unnecessary_unwrap)]
|
||||
Err(Box::new(creation_result.unwrap_err()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn xdg_runtime_directory() -> Option<PathBuf> {
|
||||
std::env::var("XDG_RUNTIME_DIR").ok().map(Into::into)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user