#![doc = include_str!("../README.md")]
#![cfg(target_os = "macos")]
use std::ffi::CString;
use std::os::unix::io::RawFd;
extern "C" {
fn launch_activate_socket(
name: *const libc::c_char,
fds: *mut *mut libc::c_int,
cnt: *mut libc::size_t,
) -> libc::c_int;
}
#[derive(Debug, Clone)]
pub enum Error {
Null,
NotInPlist,
NotManaged,
AlreadyActivated,
Unknown(libc::c_int),
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Null => f.write_str("Null found in name string."),
Error::NotInPlist => f.write_str(
"The socket name specified does not exist in the caller's launchd.plist.",
),
Error::NotManaged => f.write_str("The calling process is not managed by launchd."),
Error::AlreadyActivated => {
f.write_str("The specified socket has already been activated.")
}
Error::Unknown(x) => {
f.write_str(&format!("An unknown error occurred with code {}.", x))
}
}
}
}
pub fn activate_socket(name: &str) -> Result<Vec<RawFd>, Error> {
let c_name = CString::new(name).map_err(|_| Error::Null)?;
let mut fds: *mut libc::c_int = std::ptr::null_mut();
let mut cnt: libc::size_t = 0;
let error = unsafe { launch_activate_socket(c_name.as_ptr(), &mut fds, &mut cnt) };
match error {
0 => { }
libc::ENOENT => return Err(Error::NotInPlist),
libc::ESRCH => return Err(Error::NotManaged),
libc::EALREADY => return Err(Error::AlreadyActivated),
unknown => return Err(Error::Unknown(unknown)),
};
let out = unsafe { std::slice::from_raw_parts(fds, cnt).to_vec() };
unsafe {
libc::free(fds as *mut _);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke() {
println!("{:?}", activate_socket("noidea"));
}
}