commit 060b6ac88d2cf221b78914673b58f1d1533b3fd4 Author: finga Date: Wed Jul 7 20:23:04 2021 +0200 Implement creation of SysV IPC message queues This adds the `sysvmq` package which handles SysV IPC message queues. For consistency the parameter for `permissions` is renamed to `mode` also for POSIX message queues. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a2cee0f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sysvmq" +version = "0.1.0" +edition = "2018" +authors = ["finga "] +repository = "https://git.onders.org/finga/mqrs" +license = "GPL-3.0-or-later" + +[dependencies] +libc = "0.2.98" +thiserror = "1.0.26" +nix = "0.21.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7f7e4e3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,111 @@ +use libc::{ + msgget, IPC_CREAT, IPC_EXCL, IPC_INFO, IPC_NOWAIT, IPC_PRIVATE, IPC_RMID, IPC_SET, IPC_STAT, + MSG_COPY, MSG_EXCEPT, MSG_INFO, MSG_NOERROR, MSG_STAT, +}; +use nix::errno::{errno, Errno}; +use std::{marker::PhantomData, num::ParseIntError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum SysvMqError { + #[error("SysV message queue: {0}")] + ErrnoError(&'static str), + #[error("No message queue found with key {0}")] + KeyNotFound(i32), + #[error("IO Error: {0}")] + IoError(#[from] std::io::Error), + #[error("Parse Error: {0}")] + ParserError(#[from] ParseIntError), +} + +/// IPC bit flags +#[repr(i32)] +pub enum Flags { + /// Create key if key does not exist. + CreateKey = IPC_CREAT, + /// Fail if key exists. + Exclusive = IPC_EXCL, + /// Return error on wait. + NoWait = IPC_NOWAIT, + /// No error if message is too big. + NoError = MSG_NOERROR, + /// Receive any message except of specified type. + Except = MSG_EXCEPT, + /// Copy (not remove) all queue messages. + Copy = MSG_COPY, + /// Private key (Special key value). + Private = IPC_PRIVATE, +} + +/// Commands for `msgctl()` +#[repr(i32)] +pub enum ControlCommands { + /// Remove identifier (Control command for `msgctl`, `semctl`, and `shmctl`). + Remove = IPC_RMID, + /// Set `ipc_perm` options (Control command for `msgctl`, `semctl`, and `shmctl`). + SetPerm = IPC_SET, + /// Get `ipc_perm` options (Control command for `msgctl`, `semctl`, and `shmctl`). + GetPerm = IPC_STAT, + /// See ipcs (Control command for `msgctl`, `semctl`, and `shmctl`). + IpcInfo = IPC_INFO, + /// IPCS control command. + Stat = MSG_STAT, + /// IPCS control command. + MsgInfo = MSG_INFO, +} + +pub struct SysvMq { + pub id: i32, + pub key: i32, + message_mask: i32, + mode: i32, + types: PhantomData, +} + +impl SysvMq { + pub fn create(&mut self, key: i32) -> Result<&Self, SysvMqError> { + self.key = key; + self.id = unsafe { msgget(self.key, Flags::CreateKey as i32 | self.mode) }; + + match self.id { + -1 => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), + _ => Ok(self), + } + } + + pub fn open(mut self, key: i32) -> Result { + self.key = key; + self.id = unsafe { msgget(self.key, self.mode) }; + + match self.id { + -1 => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), + _ => Ok(self), + } + } + + pub fn mode(&mut self, mode: i32) -> &Self { + self.mode = mode; + self + } + + pub fn nonblocking(&mut self) -> &Self { + self.message_mask |= Flags::NoWait as i32; + self + } + + pub fn new() -> Self { + SysvMq { + id: -1, + key: 0, + message_mask: 0, + mode: 0o644, + types: PhantomData, + } + } +} + +impl Default for SysvMq { + fn default() -> Self { + Self::new() + } +}