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.
This commit is contained in:
finga 2021-07-07 20:23:04 +02:00
parent a468c5d7bb
commit 4d200bb5f3
11 changed files with 271 additions and 10 deletions

66
Cargo.lock generated
View file

@ -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"

View file

@ -3,9 +3,12 @@ name = "mqrs"
version = "0.1.1"
authors = ["finga <mqrs@onders.org>"]
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."

View file

@ -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 <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.

32
mqrs.1
View file

@ -62,13 +62,14 @@ Produce verbose output
.TP 8
.SS OPTIONS
.RS
.TP 8
.B \-c, \-\-capacity \fI<capacity>\fP
Maximum number of messages in the queue
.TP 8
.B \-s, \-\-msgsize \fI<msgsize>\fP
Message size in bytes
.TP 8
.B \-p, \-\-permissions \fI<permissions>\fP
.B \-m, \-\-mode \fI<mode>\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<KEY>\fP
Create a new SysV IPC message queue.
.TP 8
.SS ARGS
.RS
.TP 8
.B \fI<KEY>\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<mode>\fP
Permissions (octal) to create the queue with (default: 0644)
.RE
.SH SEE ALSO
mq_overview(7)
.SH BUGS

View file

@ -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()?,
},
}

View file

@ -9,7 +9,7 @@ use std::fs;
pub struct Create {
/// Permissions (octal) to create the queue with
#[clap(short, long)]
permissions: Option<String>,
mode: Option<String>,
/// Maximum number of messages in the queue
#[clap(short, long)]
capacity: Option<usize>,
@ -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)?);
}

3
src/sysv.rs Normal file
View file

@ -0,0 +1,3 @@
mod create;
pub use create::Create;

31
src/sysv/create.rs Normal file
View file

@ -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<String>,
/// Key of the new queue
#[clap(value_name = "KEY")]
key: i32,
}
impl Create {
pub fn run(&self) -> Result<()> {
let mut mq = SysvMq::<String>::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(())
}
}

2
sysvmq/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
Cargo.lock

12
sysvmq/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "sysvmq"
version = "0.1.0"
edition = "2018"
authors = ["finga <mqrs@onders.org>"]
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"

111
sysvmq/src/lib.rs Normal file
View file

@ -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<T> {
pub id: i32,
pub key: i32,
message_mask: i32,
mode: i32,
types: PhantomData<T>,
}
impl<T> SysvMq<T> {
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, SysvMqError> {
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<T> Default for SysvMq<T> {
fn default() -> Self {
Self::new()
}
}