mqrs/sysvmq/src/lib.rs
finga 79a2ad1d88 sysvmq: Implement send and recv and refactor
Implement sending to and receiving from SysV IPC message queues. While
at it refactor the whole library. Write documentation for the sysvmq
library.
2023-11-05 15:36:17 +01:00

356 lines
9 KiB
Rust

#![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<i32, SysvMqError> {
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::<c_void>(), 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::<c_void>(), 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<msqid_ds, SysvMqError> {
let mut ipc_info = MaybeUninit::<msqid_ds>::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::<msqid_ds>().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<Self, SysvMqError> {
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<Self, SysvMqError> {
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<msqid_ds, SysvMqError> {
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<msqid_ds, SysvMqError> {
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<msqid_ds, SysvMqError> {
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<msqid_ds, SysvMqError> {
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)
}
}