Files
fluxer-desktop-patched/fluxer_desktop/native/mac-sysctl/src/lib.rs
T
brenden 682afacd30 Add native self-hosted instance connection to fluxer_desktop
Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 +
tools/ci) with a "Connect to a Different Server" menu item and popout
that lets the desktop app switch to any self-hosted Fluxer instance,
plus fixes for well-known discovery on single-domain self-hosted
deployments and a false-positive ERR_ABORTED on same-origin client
redirects during the switch. Defaults to chat.fluxr.chat and uses an
isolated userData directory from the official build.
2026-07-01 18:22:43 -04:00

184 lines
5.4 KiB
Rust

// SPDX-License-Identifier: AGPL-3.0-or-later
use napi::{Error, Result, Status};
use napi_derive::napi;
#[cfg(target_os = "macos")]
use sysctl::{Ctl, CtlValue, Sysctl, SysctlError};
fn invalid_name_error(function_name: &str) -> Error {
Error::new(
Status::InvalidArg,
format!("{function_name} requires a non-empty name string"),
)
}
#[cfg(target_os = "macos")]
fn map_sysctl_error(error: SysctlError) -> Error {
match error {
SysctlError::IoError(ref io_error) => {
if let Some(errno) = io_error.raw_os_error() {
return Error::new(
Status::GenericFailure,
format!("sysctlbyname failed (errno {errno})"),
);
}
}
SysctlError::MissingImplementation
| SysctlError::ExtractionError
| SysctlError::ParseError
| SysctlError::InvalidCStr(_)
| SysctlError::InvalidCString(_) => {
return Error::new(
Status::GenericFailure,
"sysctlbyname failed (errno 22)".to_owned(),
);
}
SysctlError::NotFound(_)
| SysctlError::Utf8Error(_)
| SysctlError::NoReadAccess
| SysctlError::NoWriteAccess
| SysctlError::NotSupported
| SysctlError::ShortRead { .. } => {}
}
Error::new(
Status::GenericFailure,
format!("sysctlbyname failed ({error})"),
)
}
#[cfg(not(target_os = "macos"))]
fn unsupported_platform_error() -> Error {
Error::new(
Status::GenericFailure,
"sysctlbyname failed (errno 22)".to_owned(),
)
}
#[cfg(target_os = "macos")]
fn ctl_for_name(name: &str) -> Result<Option<Ctl>> {
match Ctl::new(name) {
Ok(ctl) => Ok(Some(ctl)),
Err(SysctlError::NotFound(_)) => Ok(None),
Err(error) => Err(map_sysctl_error(error)),
}
}
#[cfg(target_os = "macos")]
fn value_to_number(value: CtlValue) -> Result<Option<f64>> {
let number = match value {
CtlValue::None => return Ok(None),
CtlValue::Int(value) => f64::from(value),
CtlValue::Uint(value) => f64::from(value),
CtlValue::Long(value) => value as f64,
CtlValue::Ulong(value) => value as f64,
CtlValue::S64(value) => value as f64,
CtlValue::U64(value) => value as f64,
CtlValue::S32(value) => f64::from(value),
CtlValue::U32(value) => f64::from(value),
CtlValue::S16(value) => f64::from(value),
CtlValue::U16(value) => f64::from(value),
CtlValue::S8(value) => f64::from(value),
CtlValue::U8(value) => f64::from(value),
CtlValue::String(_) | CtlValue::Struct(_) | CtlValue::Node(_) => {
return Err(Error::new(
Status::GenericFailure,
"sysctlbyname failed (errno 22)".to_owned(),
));
}
};
Ok(Some(number))
}
#[cfg(target_os = "macos")]
fn read_int_blocking(name: String) -> Result<Option<f64>> {
let Some(ctl) = ctl_for_name(&name)? else {
return Ok(None);
};
ctl.value()
.map_err(map_sysctl_error)
.and_then(value_to_number)
}
#[cfg(not(target_os = "macos"))]
fn read_int_blocking(_: String) -> Result<Option<f64>> {
Err(unsupported_platform_error())
}
#[cfg(target_os = "macos")]
fn read_string_blocking(name: String) -> Result<Option<String>> {
let Some(ctl) = ctl_for_name(&name)? else {
return Ok(None);
};
match ctl.value().map_err(map_sysctl_error)? {
CtlValue::None => Ok(None),
CtlValue::String(value) => Ok(Some(value)),
_ => Err(Error::new(
Status::GenericFailure,
"sysctlbyname failed (errno 22)".to_owned(),
)),
}
}
#[cfg(not(target_os = "macos"))]
fn read_string_blocking(_: String) -> Result<Option<String>> {
Err(unsupported_platform_error())
}
async fn spawn_sysctl<T: Send + 'static>(
work: impl FnOnce() -> Result<T> + Send + 'static,
) -> Result<T> {
tokio::task::spawn_blocking(work).await.map_err(|error| {
Error::new(
Status::GenericFailure,
format!("sysctl worker failed: {error}"),
)
})?
}
#[napi(js_name = "sysctlByNameInt")]
pub async fn sysctl_by_name_int(name: String) -> Result<Option<f64>> {
if name.is_empty() {
return Err(invalid_name_error("sysctlByNameInt"));
}
spawn_sysctl(move || read_int_blocking(name)).await
}
#[napi(js_name = "sysctlByNameString")]
pub async fn sysctl_by_name_string(name: String) -> Result<Option<String>> {
if name.is_empty() {
return Err(invalid_name_error("sysctlByNameString"));
}
spawn_sysctl(move || read_string_blocking(name)).await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_name_error_mentions_function_name() {
let error = invalid_name_error("sysctlByNameInt");
assert_eq!(Status::InvalidArg, error.status);
assert!(error.reason.contains("sysctlByNameInt"));
}
#[cfg(not(target_os = "macos"))]
#[test]
fn non_macos_returns_same_synthetic_errno_as_previous_js_contract() {
let error = unsupported_platform_error();
assert_eq!("sysctlbyname failed (errno 22)", error.reason);
}
#[cfg(target_os = "macos")]
#[test]
fn missing_sysctl_maps_to_null() {
assert!(
ctl_for_name("fluxer.definitely_missing_sysctl")
.unwrap()
.is_none()
);
}
}