use std::{os::unix::fs::PermissionsExt, path::PathBuf, str::FromStr};
use anyhow::Context;
use serde::{Deserialize, Serialize, Serializer, de::Error as _};
use crate::endpoint::parse::ParseEndpointError;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Mode(u32);
impl Mode {
pub(super) const PRIVATE: Self = Self(0o600);
pub(super) fn apply(self, path: &std::path::Path) -> anyhow::Result<()> {
std::fs::set_permissions(path, PermissionsExt::from_mode(self.0))
.with_context(|| format!("chmod {:o} on {}", self.0, path.display()))
}
}
impl FromStr for Mode {
type Err = ParseEndpointError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let bits = u32::from_str_radix(s, 8)
.map_err(|_| ParseEndpointError::InvalidMode(s.to_string()))?;
if bits > 0o777 {
return Err(ParseEndpointError::InvalidMode(s.to_string()));
}
Ok(Mode(bits))
}
}
impl<'de> Deserialize<'de> for Mode {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let bits = u32::from_str_radix(&s, 8)
.map_err(|_| D::Error::custom(format!("invalid octal mode {s:?}")))?;
if bits > 0o777 {
return Err(D::Error::custom(format!("mode {s} out of range")));
}
Ok(Mode(bits))
}
}
impl Serialize for Mode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("{:03o}", self.0))
}
}
pub struct PathGuard(pub PathBuf);
impl Drop for PathGuard {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
#[cfg(target_os = "linux")]
pub fn size_if_pipe<F: std::os::fd::AsFd>(fd: &F, label: &str, want: usize) {
use rustix::{io::Errno, pipe::fcntl_setpipe_size};
use tracing::{debug, warn};
match fcntl_setpipe_size(fd, want) {
Ok(got) if got == want => debug!(pipe = label, size = got, "pipe resized"),
Ok(got) => debug!(
pipe = label,
want, got, "pipe resized to the next power of two"
),
Err(e) if e == Errno::INVAL => debug!(pipe = label, "not a pipe; leaving it alone"),
Err(e) if e == Errno::BUSY => debug!(pipe = label, want, "pipe busy; leaving it alone"),
Err(e) if e == Errno::PERM => warn!(
pipe = label,
want, "cannot enlarge pipe past /proc/sys/fs/pipe-max-size without CAP_SYS_RESOURCE",
),
Err(e) => debug!(pipe = label, error = %e, "could not resize pipe"),
}
}
#[cfg(not(target_os = "linux"))]
pub fn size_if_pipe<F: std::os::fd::AsFd>(_fd: &F, _label: &str, _want: usize) {
}
pub(super) fn default_true() -> bool {
true
}