From 4d200bb5f3d03d10e55672c2ee3ec398a89a67a4 Mon Sep 17 00:00:00 2001 From: finga Date: Wed, 7 Jul 2021 20:23:04 +0200 Subject: [PATCH] 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. --- Cargo.lock | 66 ++++++++++++++++++++++++-- Cargo.toml | 7 +++ README.md | 10 +++- mqrs.1 | 32 ++++++++++++- src/main.rs | 3 ++ src/posix/create.rs | 4 +- src/sysv.rs | 3 ++ src/sysv/create.rs | 31 +++++++++++++ sysvmq/.gitignore | 2 + sysvmq/Cargo.toml | 12 +++++ sysvmq/src/lib.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 271 insertions(+), 10 deletions(-) create mode 100644 src/sysv.rs create mode 100644 src/sysv/create.rs create mode 100644 sysvmq/.gitignore create mode 100644 sysvmq/Cargo.toml create mode 100644 sysvmq/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index afa11c4..3c4a517 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "cc" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" + [[package]] name = "cfg-if" version = "1.0.0" @@ -152,9 +158,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.97" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "log" @@ -171,6 +177,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + [[package]] name = "mqrs" version = "0.1.1" @@ -182,6 +197,20 @@ dependencies = [ "humantime", "log", "posixmq", + "sysvmq", +] + +[[package]] +name = "nix" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3728fec49d363a50a8828a190b379a446cc5cf085c06259bbbeb34447e4ec7" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", ] [[package]] @@ -294,6 +323,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "sysvmq" +version = "0.1.0" +dependencies = [ + "libc", + "nix", + "thiserror", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -312,6 +350,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.1.44" @@ -325,9 +383,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" diff --git a/Cargo.toml b/Cargo.toml index a42c1c3..edbaea8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,12 @@ name = "mqrs" version = "0.1.1" authors = ["finga "] edition = "2018" +repository = "https://git.onders.org/finga/mqrs" license = "GPL-3.0-or-later" readme = "README.md" description = "A CLI program for interacting with Posix Message Queues." +keywords = ["message_queue", "mq", "mqueue", "queue"] +categories = ["command-line-utilities"] [dependencies] anyhow = "1.0" @@ -15,6 +18,10 @@ chrono = "0.4" humantime = "2.1" log = "0.4" env_logger = "0.8" +sysvmq = { path = "sysvmq" } + +[workspace] +members = ["sysvmq"] [package.metadata.deb] extended-description = "`mqrs` is a small cli application to handle POSIX message queues." diff --git a/README.md b/README.md index 588dfe4..4f82cfa 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The POSIX backend supports six commands: `create`, `info`, `list`, Use the `create` command to create a new POSIX message queue. Following optional arguments are supported: - `-c`, `--capacity`: Maximum number of messages in the queue -- `-p`, `--permissions`: Permissions (octal) to create the queue with +- `-m`, `--mode`: Permissions (octal) to create the queue with - `-s`, `--msgsize`: Message size in bytes #### Print information about a message queue @@ -58,4 +58,10 @@ queue. Following optional arguments are supported: - `-o,` `--timeout `: As for example in "5h 23min 42ms" ### SysV IPC message queues -The SysV IPC backend supports no commands yet. +The SysV IPC backend supports one command: `create`. + +#### Create a message queue +Use the `create` command to create a new SysV IPC message +queue. Following optional arguments are supported: +- `-m`, `--mode`: Permissions (octal) to create the queue + with. Default: 0644. diff --git a/mqrs.1 b/mqrs.1 index 9934081..0058957 100644 --- a/mqrs.1 +++ b/mqrs.1 @@ -62,13 +62,14 @@ Produce verbose output .TP 8 .SS OPTIONS .RS +.TP 8 .B \-c, \-\-capacity \fI\fP Maximum number of messages in the queue .TP 8 .B \-s, \-\-msgsize \fI\fP Message size in bytes .TP 8 -.B \-p, \-\-permissions \fI\fP +.B \-m, \-\-mode \fI\fP Permissions (octal) to create the queue with .RE .SS help [SUBCOMMAND] @@ -210,7 +211,34 @@ Prints help information Produce verbose output .RE .SH SYSV IPC MESSAGE QUEUE SUBCOMMANDS -The SysV IPC backend supports no commands yet. +The SysV IPC backend supports one command: +.B create\ +. +.SS create [FLAGS] [OPTIONS] \fI\fP +Create a new SysV IPC message queue. +.TP 8 +.SS ARGS +.RS +.TP 8 +.B \fI\fP +Key of the new queue +.RE +.TP 8 +.SS FLAGS +.RS +.TP 8 +.B \-h, \-\-help +Prints help information +.TP 8 +.B \-v, \-\-verbose +Produce verbose output +.TP 8 +.SS OPTIONS +.RS +.TP 8 +.B \-m, \-\-mode \fI\fP +Permissions (octal) to create the queue with (default: 0644) +.RE .SH SEE ALSO mq_overview(7) .SH BUGS diff --git a/src/main.rs b/src/main.rs index cc1d185..78109dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use anyhow::Result; use clap::{crate_authors, crate_version, AppSettings, Clap}; mod posix; +mod sysv; #[derive(Clap, Debug)] enum Backend { @@ -23,6 +24,7 @@ enum PosixCommand { #[derive(Clap, Debug)] enum SysvCommand { + Create(sysv::Create), } #[derive(Clap, Debug)] @@ -65,6 +67,7 @@ fn main() -> Result<()> { PosixCommand::Recv(r) => r.run()?, }, Backend::Sysv(s) => match s { + SysvCommand::Create(c) => c.run()?, }, } diff --git a/src/posix/create.rs b/src/posix/create.rs index d475d36..be4606b 100644 --- a/src/posix/create.rs +++ b/src/posix/create.rs @@ -9,7 +9,7 @@ use std::fs; pub struct Create { /// Permissions (octal) to create the queue with #[clap(short, long)] - permissions: Option, + mode: Option, /// Maximum number of messages in the queue #[clap(short, long)] capacity: Option, @@ -39,7 +39,7 @@ impl Create { pub fn run(&self) -> Result<()> { let mq = &mut posixmq::OpenOptions::readonly(); - if let Some(m) = &self.permissions { + if let Some(m) = &self.mode { mq.mode(u32::from_str_radix(&m, 8)?); } diff --git a/src/sysv.rs b/src/sysv.rs new file mode 100644 index 0000000..5ed245c --- /dev/null +++ b/src/sysv.rs @@ -0,0 +1,3 @@ +mod create; + +pub use create::Create; diff --git a/src/sysv/create.rs b/src/sysv/create.rs new file mode 100644 index 0000000..e4b03a6 --- /dev/null +++ b/src/sysv/create.rs @@ -0,0 +1,31 @@ +use anyhow::Result; +use clap::Clap; +use log::info; +use sysvmq::SysvMq; + +/// Create a SysV message queue +#[derive(Clap, Debug)] +pub struct Create { + /// Permissions (octal) to create the queue with (default: 0644) + #[clap(short, long)] + mode: Option, + /// Key of the new queue + #[clap(value_name = "KEY")] + key: i32, +} + +impl Create { + pub fn run(&self) -> Result<()> { + let mut mq = SysvMq::::new(); + + if let Some(m) = &self.mode { + mq.mode(i32::from_str_radix(&m, 8)?); + } + + mq.create(self.key)?; + + info!("SysV message queue created, key: {}, id: {}", mq.key, mq.id); + + Ok(()) + } +} diff --git a/sysvmq/.gitignore b/sysvmq/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/sysvmq/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/sysvmq/Cargo.toml b/sysvmq/Cargo.toml new file mode 100644 index 0000000..a2cee0f --- /dev/null +++ b/sysvmq/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/sysvmq/src/lib.rs b/sysvmq/src/lib.rs new file mode 100644 index 0000000..7f7e4e3 --- /dev/null +++ b/sysvmq/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() + } +}