maolan-plugin-protocol 0.0.6

Shared protocol types for Maolan out-of-process plugin hosting
Documentation
#[cfg(unix)]
use std::ffi::CString;

pub struct ShmMapping {
    ptr: *mut u8,
    size: usize,
    #[allow(dead_code)]
    name: String,
    #[cfg(windows)]
    handle: *mut std::ffi::c_void,
}

unsafe impl Send for ShmMapping {}
unsafe impl Sync for ShmMapping {}

impl ShmMapping {
    #[cfg(unix)]
    pub fn create(name: &str, size: usize) -> Result<Self, String> {
        let c_name = CString::new(name).map_err(|e| e.to_string())?;
        let fd = unsafe { libc::shm_open(c_name.as_ptr(), libc::O_CREAT | libc::O_RDWR, 0o644) };
        if fd < 0 {
            return Err(format!(
                "shm_open({}, O_CREAT|O_RDWR) failed: {:?}",
                name,
                std::io::Error::last_os_error()
            ));
        }
        if unsafe { libc::ftruncate(fd, size as libc::off_t) } < 0 {
            unsafe { libc::close(fd) };
            return Err(format!(
                "ftruncate failed: {:?}",
                std::io::Error::last_os_error()
            ));
        }
        let ptr = unsafe {
            libc::mmap(
                std::ptr::null_mut(),
                size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                fd,
                0,
            )
        };
        unsafe { libc::close(fd) };
        if ptr == libc::MAP_FAILED {
            unsafe { libc::shm_unlink(c_name.as_ptr()) };
            return Err(format!(
                "mmap failed: {:?}",
                std::io::Error::last_os_error()
            ));
        }
        Ok(Self {
            ptr: ptr as *mut u8,
            size,
            name: name.to_string(),
        })
    }

    #[cfg(unix)]
    pub fn open_existing(name: &str, size: usize) -> Result<Self, String> {
        let c_name = CString::new(name).map_err(|e| e.to_string())?;
        let fd = unsafe { libc::shm_open(c_name.as_ptr(), libc::O_RDWR, 0) };
        if fd < 0 {
            return Err(format!(
                "shm_open({}, O_RDWR) failed: {:?}",
                name,
                std::io::Error::last_os_error()
            ));
        }
        let ptr = unsafe {
            libc::mmap(
                std::ptr::null_mut(),
                size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                fd,
                0,
            )
        };
        unsafe { libc::close(fd) };
        if ptr == libc::MAP_FAILED {
            return Err(format!(
                "mmap failed: {:?}",
                std::io::Error::last_os_error()
            ));
        }
        Ok(Self {
            ptr: ptr as *mut u8,
            size,
            name: name.to_string(),
        })
    }

    #[cfg(windows)]
    pub fn create(name: &str, size: usize) -> Result<Self, String> {
        use windows_sys::Win32::Foundation::{GetLastError, INVALID_HANDLE_VALUE};
        use windows_sys::Win32::System::Memory::{
            CreateFileMappingW, FILE_MAP_ALL_ACCESS, MapViewOfFile, PAGE_READWRITE,
        };

        let wide_name: Vec<u16> = format!("Local\\{}", name)
            .encode_utf16()
            .chain(std::iter::once(0))
            .collect();
        let handle = unsafe {
            CreateFileMappingW(
                INVALID_HANDLE_VALUE,
                std::ptr::null_mut(),
                PAGE_READWRITE,
                0,
                size as u32,
                wide_name.as_ptr(),
            )
        };
        if handle.is_null() {
            return Err(format!("CreateFileMappingW failed: {}", unsafe {
                GetLastError()
            }));
        }
        let ptr = unsafe { MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size) };
        if ptr.Value.is_null() {
            unsafe { windows_sys::Win32::Foundation::CloseHandle(handle) };
            return Err(format!("MapViewOfFile failed: {}", unsafe {
                GetLastError()
            }));
        }
        Ok(Self {
            ptr: ptr.Value as *mut u8,
            size,
            name: name.to_string(),
            handle,
        })
    }

    #[cfg(windows)]
    pub fn open_existing(name: &str, size: usize) -> Result<Self, String> {
        use windows_sys::Win32::Foundation::{CloseHandle, GetLastError};
        use windows_sys::Win32::System::Memory::{
            FILE_MAP_ALL_ACCESS, MapViewOfFile, OpenFileMappingW,
        };

        let wide_name: Vec<u16> = format!("Local\\{}", name)
            .encode_utf16()
            .chain(std::iter::once(0))
            .collect();
        let handle = unsafe { OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, wide_name.as_ptr()) };
        if handle.is_null() {
            return Err(format!("OpenFileMappingW failed: {}", unsafe {
                GetLastError()
            }));
        }
        let ptr = unsafe { MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size) };
        if ptr.Value.is_null() {
            unsafe { CloseHandle(handle) };
            return Err(format!("MapViewOfFile failed: {}", unsafe {
                GetLastError()
            }));
        }
        Ok(Self {
            ptr: ptr.Value as *mut u8,
            size,
            name: name.to_string(),
            handle,
        })
    }

    pub fn as_ptr(&self) -> *mut u8 {
        self.ptr
    }

    pub fn size(&self) -> usize {
        self.size
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    #[cfg(unix)]
    pub fn unlink(name: &str) -> Result<(), String> {
        let c_name = CString::new(name).map_err(|e| e.to_string())?;
        let res = unsafe { libc::shm_unlink(c_name.as_ptr()) };
        if res < 0 {
            Err(format!(
                "shm_unlink failed: {:?}",
                std::io::Error::last_os_error()
            ))
        } else {
            Ok(())
        }
    }

    #[cfg(windows)]
    pub fn unlink(_name: &str) -> Result<(), String> {
        Ok(())
    }
}

#[cfg(unix)]
impl Drop for ShmMapping {
    fn drop(&mut self) {
        if !self.ptr.is_null() && self.ptr != libc::MAP_FAILED as *mut u8 {
            unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.size) };
        }
    }
}

#[cfg(windows)]
impl Drop for ShmMapping {
    fn drop(&mut self) {
        use windows_sys::Win32::Foundation::CloseHandle;
        use windows_sys::Win32::System::Memory::UnmapViewOfFile;
        if !self.ptr.is_null() {
            unsafe {
                UnmapViewOfFile(
                    windows_sys::Win32::System::Memory::MEMORY_MAPPED_VIEW_ADDRESS {
                        Value: self.ptr as *mut std::ffi::c_void,
                    },
                )
            };
        }
        if !self.handle.is_null() {
            unsafe { CloseHandle(self.handle) };
        }
    }
}