#![allow(clippy::doc_markdown)] //! This library provides a convenient API to SysV IPC message queues. //! //! # Example //! //! ```rust //! use sysvmq::{SysvMq, SysvMqError}; //! //! fn example() -> Result<(), SysvMqError> { //! let mut mq = SysvMq::new(0)?; //! let mut buf = [0u8; 11]; //! //! mq.send(b"hello queue")?; //! mq.recv(&mut buf)?; //! mq.delete()?; //! //! Ok(()) //! } //! ``` use libc::{ c_void, msgctl, msgget, msgrcv, msgsnd, msqid_ds, IPC_CREAT, IPC_NOWAIT, IPC_RMID, IPC_SET, }; pub use libc::{IPC_INFO, IPC_STAT, MSG_INFO, MSG_STAT}; use nix::errno::{errno, Errno}; use std::{convert::TryFrom, mem::MaybeUninit, ptr}; use thiserror::Error; #[derive(Debug, Error)] /// An enum containing all errors pub enum SysvMqError { #[error("SysV message queue: {0}")] ErrnoError(&'static str), #[error("Cannot convert integer")] From(#[from] std::num::TryFromIntError), } /// Low level function to create a new SysV IPC message queue. /// /// # Example /// /// ```rust /// # use sysvmq::delete; /// use sysvmq::{create, SysvMqError}; /// /// fn main() -> Result<(), SysvMqError> { /// let mq_id = create(0, 0o644)?; /// println!("new queue: {mq_id}"); /// # delete(mq_id)?; /// /// Ok(()) /// } /// ``` /// /// # Errors /// /// Return `SysvMqError` when the queue cannot be created. pub fn create(key: i32, mode: i32) -> Result { let mq = unsafe { msgget(key, IPC_CREAT | mode) }; match mq { -1 => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), id => Ok(id), } } /// Low level function to send a message to a SysV IPC message queue. /// /// # Example /// /// ```rust /// # use sysvmq::{delete}; /// use sysvmq::{create, send, SysvMqError}; /// /// fn main() -> Result<(), SysvMqError> { /// let mq_id = create(0, 0o644)?; /// let msg = b"hello queue"; /// send(mq_id, msg, 0)?; /// # delete(mq_id)?; /// /// Ok(()) /// } /// ``` /// /// # Errors /// /// Return `SysvMqError` when the message cannot be sent to the queue. pub fn send(id: i32, msg: &[u8], mask: i32) -> Result<(), SysvMqError> { match unsafe { msgsnd(id, msg.as_ptr().cast::(), msg.len(), mask) } { -1 => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), _ => Ok(()), } } /// Low level function to receive a message from a SysV IPC message /// queue. /// /// # Example /// /// ```rust /// # use sysvmq::{delete, send}; /// use sysvmq::{create, recv, SysvMqError}; /// /// fn main() -> Result<(), SysvMqError> { /// let mq_mask = 0o644; /// let mq_id = create(0, mq_mask)?; /// let mut buf = [0u8; 32]; /// # let msg = b"hello queue"; /// # send(mq_id, msg, 0)?; /// recv(mq_id, &mut buf, mq_mask)?; /// println!("received message: {:?}", buf); /// # delete(mq_id)?; /// /// Ok(()) /// } /// ``` /// /// # Errors /// /// Return `SysvMqError` when the message cannot be received from the /// queue. pub fn recv(id: i32, msg: &mut [u8], mask: i32) -> Result<(), SysvMqError> { match unsafe { msgrcv(id, msg.as_mut_ptr().cast::(), msg.len(), 0, mask) } { -1 => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), _ => Ok(()), } } /// Low level function to get parameters of a SysV IPC message queue. /// /// # Example /// /// ```rust /// # use sysvmq::{delete}; /// use sysvmq::{create, get, IPC_STAT, SysvMqError}; /// /// fn main() -> Result<(), SysvMqError> { /// let mq_id = create(0, 0o644)?; /// let mq_stat = get(mq_id, IPC_STAT)?; /// println!("received message: {:?}", mq_stat); /// # delete(mq_id)?; /// /// Ok(()) /// } /// ``` /// /// # Errors /// /// Return `SysvMqError` when the parameters cannot be gathered. pub fn get(id: i32, cmd: i32) -> Result { let mut ipc_info = MaybeUninit::::uninit(); let ret; let ipc_info = unsafe { ret = msgctl(id, cmd, ipc_info.as_mut_ptr()); ipc_info.assume_init() }; match ret { 0 => Ok(ipc_info), _ => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), } } /// Low level function to set parameters of a SysV IPC message queue. /// /// # Example /// /// ```rust /// # use sysvmq::{delete}; /// use sysvmq::{create, get, IPC_STAT, set, SysvMqError}; /// /// fn main() -> Result<(), SysvMqError> { /// let mq_id = create(0, 0o644)?; /// let mut mq_stat = get(mq_id, IPC_STAT)?; /// mq_stat.msg_perm.mode = 0o666; /// set(mq_id, &mut mq_stat)?; /// # delete(mq_id)?; /// /// Ok(()) /// } /// ``` /// /// # Errors /// /// Return `SysvMqError` when the parameters cannot be set. pub fn set(id: i32, data: &mut msqid_ds) -> Result<(), SysvMqError> { match unsafe { msgctl(id, IPC_SET, data) } { 0 => Ok(()), _ => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), } } /// Low level function to delete a SysV IPC message queue. /// /// # Example /// /// ```rust /// use sysvmq::{create, delete, SysvMqError}; /// /// fn main() -> Result<(), SysvMqError> { /// let mq_id = create(0, 0o644)?; /// delete(mq_id)?; /// /// Ok(()) /// } /// ``` /// /// # Errors /// /// Return `SysvMqError` when no queue with the given key can be /// found. pub fn delete(id: i32) -> Result<(), SysvMqError> { let res = unsafe { msgctl(id, IPC_RMID, ptr::null::().cast_mut()) }; match res { -1 => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), _ => Ok(()), } } #[derive(Clone, Debug)] /// Struct representation of a Message Queue pub struct SysvMq { pub key: i32, pub id: i32, pub mask: i32, pub mode: i32, } impl Default for SysvMq { fn default() -> Self { Self { key: 0, id: -1, mask: 0, mode: 0o644, } } } impl SysvMq { /// Create a new message SysV IPC message queue with the given /// key. /// /// # Errors /// /// Return `SysvMqError` when the queue cannot be created. pub fn new(key: i32) -> Result { let mut mq = Self::default(); mq.key = key; mq.id = create(mq.key, mq.mode)?; Ok(mq) } /// Open an existing SysV IPC message queye with the given key. /// /// # Errors /// /// Return `SysvMqError` when the queue cannot be opened. pub fn open(key: i32, id: i32) -> Result { let mut mq = Self::default(); mq.key = key; mq.id = id; mq.mode = i32::from(get(mq.id, IPC_STAT)?.msg_perm.mode); Ok(mq) } /// Set the mode of a SysV IPC message queue. /// /// # Errors /// /// Return `SysvMqError` when the mode of the queue cannot be set. pub fn mode(&mut self, mode: i32) -> Result<(), SysvMqError> { self.mode = mode; let mut stats = get(self.id, IPC_STAT)?; stats.msg_perm.mode = u16::try_from(self.mode)?; set(self.id, &mut stats) } /// Set the mask of a SysV IPC message queue to nonblocking /// (`IPC_NOWAIT`). pub fn nonblocking(&mut self) { self.mask |= IPC_NOWAIT; } /// Send a message to a SysV IPC message queue. /// /// # Errors /// /// Return `SysvMqError` when the message cannot be sent to the queue. pub fn send(&mut self, msg: &[u8]) -> Result<(), SysvMqError> { send(self.id, msg, self.mask) } /// Receive a message from a SysV IPC message queue. /// /// # Errors /// /// Return `SysvMqError` when the message cannot be received from the /// queue. pub fn recv(&mut self, msg: &mut [u8]) -> Result<(), SysvMqError> { recv(self.id, msg, self.mask) } /// Receive stats from a SysV IPC message queue. /// /// # Errors /// /// Return `SysvMqError` when the stats cannot be gathered from /// the queue. pub fn stat(&self) -> Result { get(self.id, IPC_STAT) } /// Receive info from a SysV IPC message queue. /// /// # Errors /// /// Return `SysvMqError` when the info cannot be gathered from the /// queue. pub fn info(&self) -> Result { get(self.id, IPC_INFO) } /// Receive message stats from a SysV IPC message queue. /// /// # Errors /// /// Return `SysvMqError` when the message stats cannot be gathered /// from the queue. pub fn msg_stat(&self) -> Result { get(self.id, MSG_STAT) } /// Receive message info from a SysV IPC message queue. /// /// # Errors /// /// Return `SysvMqError` when the message info cannot be gathered /// from the queue. pub fn msg_info(&self) -> Result { get(self.id, MSG_INFO) } /// Delete an existing SysV IPC message queue. /// /// # Errors /// /// Return an `SysvMqError` when no queue with the given key can be /// found. pub fn delete(&mut self) -> Result<(), SysvMqError> { delete(self.id) } }