diff --git a/README.md b/README.md index e7251f7..9e73663 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # mqrs -`mqrs` is a small cli application to handle POSIX message queues. +`mqrs` is a small cli application to handle different kinds of message +queues. ## Install `mqrs` For information about how to build, install and run `mqrs` please see @@ -8,8 +9,8 @@ For information about how to build, install and run `mqrs` please see ## Using `mqrs` Depending on which backend you want to use there are different subsets of subcommands. Following backends are supported: -- `posix`: Uses POSIX message queues -- `sysv`: Uses SysV IPC message queues +- `posix`: Use POSIX message queues +- `sysv`: Use SysV IPC message queues If a command is clearly distinguishable from all the others, it does not have to be completed further. @@ -66,6 +67,16 @@ queue. Following optional arguments are supported: - `-m`, `--mode`: Permissions (octal) to create the queue with. Default: 0644. +#### Print information about a message queue +Use the `info` command to print further information about a message +queue. Exactly of the following arguments is mandatory: +- `-i`, `--id id`: Id of the queue +- `-k`, `--key key`: Key of the queue + +#### List all message queues +Use the `list` command to print a list of all message queues. No +further arguments are supported. + #### Delete a message queue Use the `unlink` command to delete a message queue. This can either be done by providing a `key` or an `id` of the queue: diff --git a/mqrs.1 b/mqrs.1 index eb7b912..45116a5 100644 --- a/mqrs.1 +++ b/mqrs.1 @@ -41,7 +41,7 @@ The POSIX backend supports six commands: and .B recv . -.SS create [FLAGS] [OPTIONS] \fI\fP +.SS posix create [FLAGS] [OPTIONS] \fI\fP Create a new POSIX message queue. .TP 8 .SS ARGS @@ -72,7 +72,7 @@ Message size in bytes .B \-m, \-\-mode \fI\fP Permissions (octal) to create the queue with .RE -.SS help [SUBCOMMAND] +.SS posix help [SUBCOMMAND] Prints this message or the help of the given subcommand. .TP 8 .SS ARGS @@ -81,7 +81,7 @@ Prints this message or the help of the given subcommand. .B \fI\fP Show help for \fISUBCOMMAND\fP .RE -.SS info [FLAGS] \fI\fP +.SS posix info [FLAGS] \fI\fP Print further information about an existing message queue. .TP 8 .SS ARGS @@ -100,7 +100,7 @@ Prints help information .B \-v, \-\-verbose Produce verbose output .RE -.SS list [FLAGS] +.SS posix list [FLAGS] Print a list of all existing POSIX message queues. .TP 8 .SS FLAGS @@ -115,7 +115,7 @@ Produce verbose output .B \-a, \-\-all Print all available information .RE -.SS recv [FLAGS] [OPTIONS] \fI\fP +.SS posix recv [FLAGS] [OPTIONS] \fI\fP Receive and print one or more messages message from a message queue. .TP 8 .SS ARGS @@ -153,7 +153,7 @@ Deadline until messages are received (format: "%Y-%m-%d %H:%M:%S") .B \-o, \-\-timeout \fI\fP Timeout as for example in "5h 23min 42ms" .RE -.SS send [FLAGS] [OPTIONS] \fI\fP \fI\fP +.SS posix send [FLAGS] [OPTIONS] \fI\fP \fI\fP Send a message to a message queue. .TP 8 .SS ARGS @@ -191,7 +191,7 @@ Set a different priority than default, priority >= 0 [default: 0] .B \-o, \-\-timeout \fI\fP Timeout as for example in "5h 23min 42ms" .RE -.SS unlink [FLAGS] \fI\fP +.SS posix unlink [FLAGS] \fI\fP Delete an existing POSIX message queue. .TP 8 .SS ARGS @@ -211,12 +211,16 @@ Prints help information Produce verbose output .RE .SH SYSV IPC MESSAGE QUEUE SUBCOMMANDS -The SysV IPC backend supports two commands: +The SysV IPC backend supports four commands: .B create\ +, +.B info\ +, +.B list and .B unlink\ . -.SS create [FLAGS] [OPTIONS] \fI\fP +.SS sysv create [FLAGS] [OPTIONS] \fI\fP Create a new SysV IPC message queue. .TP 8 .SS ARGS @@ -241,7 +245,51 @@ Produce verbose output .B \-m, \-\-mode \fI\fP Permissions (octal) to create the queue with (default: 0644) .RE -.SS unlink [FLAGS] [OPTIONS] +.SS sysv help [SUBCOMMAND] +Prints this message or the help of the given subcommand. +.TP 8 +.SS ARGS +.RS +.TP 8 +.B \fI\fP +Show help for \fISUBCOMMAND\fP +.RE +.SS sysv info [FLAGS] [OPTIONS] +Print further information about an existing message queue. Exactly of +the OPTION arguments is mandatory. +.TP 8 +.SS FLAGS +.RS +.TP 8 +.B \-h, \-\-help +Prints help information +.TP 8 +.B \-v, \-\-verbose +Produce verbose output +.RE +.TP 8 +.SS OPTIONS +.RS +.TP 8 +.B \-i, \-\-id \fI\fP +Id of the queue +.TP 8 +.B \-k, \-\-key \fI\fP +Key of the queue +.RE +.SS sysv list [FLAGS] +Print a list of all existing SysV IPC message queues. +.TP 8 +.SS FLAGS +.RS +.TP 8 +.B \-h, \-\-help +Prints help information +.TP 8 +.B \-v, \-\-verbose +Produce verbose output +.RE +.SS sysv unlink [FLAGS] [OPTIONS] Delete an existing SysV IPC message queue. It is mandatory to pass exactly one OPTION. .TP 8 diff --git a/src/main.rs b/src/main.rs index 5e4db83..b2dc94b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,8 @@ enum PosixCommand { #[derive(Clap, Debug)] enum SysvCommand { Create(sysv::Create), + Info(sysv::Info), + List(sysv::List), Unlink(sysv::Unlink), } @@ -69,6 +71,8 @@ fn main() -> Result<()> { }, Backend::Sysv(s) => match s { SysvCommand::Create(c) => c.run()?, + SysvCommand::Info(i) => i.run()?, + SysvCommand::List(l) => l.run()?, SysvCommand::Unlink(u) => u.run()?, }, } diff --git a/src/posix/info.rs b/src/posix/info.rs index 0fd1e76..abf9641 100644 --- a/src/posix/info.rs +++ b/src/posix/info.rs @@ -7,7 +7,7 @@ use posixmq::PosixMq; pub struct Info { /// Name of the queue #[clap(value_name = "QUEUE")] - pub queue: String, + queue: String, } impl Info { diff --git a/src/posix/list.rs b/src/posix/list.rs index 54c031a..f9c02b6 100644 --- a/src/posix/list.rs +++ b/src/posix/list.rs @@ -4,7 +4,7 @@ use clap::Clap; use log::warn; use std::{fs, os::unix::fs::PermissionsExt}; -/// Print information about an existing message queue +/// Print a list of existing message queues #[derive(Clap, Debug)] pub struct List { /// Show all parameters diff --git a/src/posix/recv.rs b/src/posix/recv.rs index 0b8563a..6d83cef 100644 --- a/src/posix/recv.rs +++ b/src/posix/recv.rs @@ -11,22 +11,22 @@ use std::str; pub struct Recv { /// Do not block #[clap(short, long)] - pub non_blocking: bool, + non_blocking: bool, /// Print messages as they are received #[clap(short, long)] - pub follow: bool, + follow: bool, /// Print a timestamp before each message #[clap(short, long)] - pub timestamp: bool, + timestamp: bool, /// Timeout, example "5h 23min 42ms" #[clap(short = 'o', long, conflicts_with = "deadline")] - pub timeout: Option, + timeout: Option, /// Deadline until messages are received (format: "%Y-%m-%d %H:%M:%S") #[clap(short, long, conflicts_with = "timeout")] - pub deadline: Option, + deadline: Option, /// Name of the queue #[clap(value_name = "QUEUE")] - pub queue: String, + queue: String, } fn print_message(priority: u32, length: usize, timestamp: bool, msg: &str) { diff --git a/src/posix/send.rs b/src/posix/send.rs index 917679b..d6feefc 100644 --- a/src/posix/send.rs +++ b/src/posix/send.rs @@ -9,22 +9,22 @@ use log::info; pub struct Send { /// Set a different priority, priority >= 0 #[clap(short, long, default_value = "0")] - pub priority: u32, + priority: u32, /// Do not block #[clap(short, long)] - pub non_blocking: bool, + non_blocking: bool, /// Timeout, example "5h 23min 42ms" #[clap(short = 'o', long, conflicts_with = "deadline")] - pub timeout: Option, + timeout: Option, /// Deadline until messages are sent (format: "%Y-%m-%d %H:%M:%S") #[clap(short, long, conflicts_with = "timeout")] - pub deadline: Option, + deadline: Option, /// Name of the queue #[clap(value_name = "QUEUE")] - pub queue: String, + queue: String, /// Message to be sent to the queue #[clap(value_name = "MESSAGE")] - pub msg: String, + msg: String, } impl Send { diff --git a/src/sysv.rs b/src/sysv.rs index 91ee1a5..7270e78 100644 --- a/src/sysv.rs +++ b/src/sysv.rs @@ -1,5 +1,9 @@ mod create; +mod info; +mod list; mod unlink; pub use create::Create; +pub use info::Info; +pub use list::List; pub use unlink::Unlink; diff --git a/src/sysv/info.rs b/src/sysv/info.rs new file mode 100644 index 0000000..42d9fac --- /dev/null +++ b/src/sysv/info.rs @@ -0,0 +1,53 @@ +use anyhow::Result; +use clap::Clap; +use std::{ + fs::File, + io::{BufRead, BufReader}, +}; + +/// Print information about an existing message queue +#[derive(Clap, Debug)] +pub struct Info { + /// Id of the queue + #[clap(short, long, required_unless_present_any = &["key"], conflicts_with = "key")] + id: Option, + /// Key of the queue + #[clap(short, long, required_unless_present_any = &["id"], conflicts_with = "id")] + key: Option, +} + +fn print_line(line: &str) { + for field in line.split_whitespace().collect::>() { + print!("{0: <10}", field); + } + + println!(); +} + +impl Info { + pub fn run(&self) -> Result<()> { + let mut lines = BufReader::new(File::open("/proc/sysvipc/msg")?).lines(); + + print_line(&lines.nth(0).unwrap_or(Ok(String::new()))?); + + for line in lines { + let line = line?; + + if let Some(id) = self.id { + if id == line.split_whitespace().collect::>()[1].parse::()? { + print_line(&line); + + break; + } + } else if let Some(key) = self.key { + if key == line.split_whitespace().collect::>()[0].parse::()? { + print_line(&line); + + break; + } + } + } + + Ok(()) + } +} diff --git a/src/sysv/list.rs b/src/sysv/list.rs new file mode 100644 index 0000000..9f66951 --- /dev/null +++ b/src/sysv/list.rs @@ -0,0 +1,24 @@ +use anyhow::Result; +use clap::Clap; +use std::{ + fs::File, + io::{BufRead, BufReader}, +}; + +/// Print a list of existing message queues +#[derive(Clap, Debug)] +pub struct List {} + +impl List { + pub fn run(&self) -> Result<()> { + for line in BufReader::new(File::open("/proc/sysvipc/msg")?).lines() { + for field in line?.split_whitespace().collect::>() { + print!("{0: <10}", field); + } + + println!(); + } + + Ok(()) + } +} diff --git a/src/sysv/unlink.rs b/src/sysv/unlink.rs index 277edb6..8723d20 100644 --- a/src/sysv/unlink.rs +++ b/src/sysv/unlink.rs @@ -7,15 +7,15 @@ use log::info; pub struct Unlink { /// Id of the queue #[clap( - long, short, + long, required_unless_present_any = &["key"], conflicts_with = "key" )] - pub id: Option, + id: Option, /// Key of the queue - #[clap(long, short, required_unless_present_any = &["id"], conflicts_with = "id")] - pub key: Option, + #[clap(short, long, required_unless_present_any = &["id"], conflicts_with = "id")] + key: Option, } impl Unlink { @@ -25,9 +25,11 @@ impl Unlink { info!("Removed message queue with id: {}", id); } else if let Some(key) = self.key { - sysvmq::unlink_key(key)?; + let id = sysvmq::id_from_key(key)?; - info!("Removed message queue key: {}", key); + sysvmq::unlink_id(id)?; + + info!("Removed message queue key: {} (id: {})", key, id); } Ok(()) diff --git a/sysvmq/src/lib.rs b/sysvmq/src/lib.rs index bb2c21d..298a48e 100644 --- a/sysvmq/src/lib.rs +++ b/sysvmq/src/lib.rs @@ -1,21 +1,15 @@ use libc::{ - msgctl, msgget, msqid_ds, 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, + msgctl, msgget, msginfo, msqid_ds, 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, ptr}; +use std::{marker::PhantomData, mem::MaybeUninit, ptr}; 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 @@ -69,10 +63,63 @@ pub fn unlink_id(id: i32) -> Result<(), SysvMqError> { } } -pub fn unlink_key(key: i32) -> Result<(), SysvMqError> { +pub fn id_from_key(key: i32) -> Result { let id = unsafe { msgget(key, 0) }; - unlink_id(id) + match id { + -1 => Err(SysvMqError::ErrnoError(Errno::from_i32(errno()).desc())), + id => Ok(id), + } +} + +pub fn ipc_info(id: i32) -> Result<(), SysvMqError> { + let mut msginfo = MaybeUninit::::uninit(); + + unsafe { + msgctl( + id, + ControlCommands::IpcInfo as i32, + msginfo.as_mut_ptr() as *mut msqid_ds, + ); + } + + let msginfo = unsafe { msginfo.assume_init() }; + + println!("info: {:?}", msginfo); + + Ok(()) +} + +pub fn stat_info(id: i32) -> Result<(), SysvMqError> { + let mut msginfo = MaybeUninit::::uninit(); + + unsafe { + msgctl(id, ControlCommands::Stat as i32, msginfo.as_mut_ptr()); + } + + let msginfo = unsafe { msginfo.assume_init() }; + + println!("info: {:?}", msginfo); + + Ok(()) +} + +pub fn msg_info(id: i32) -> Result<(), SysvMqError> { + let mut msginfo = MaybeUninit::::uninit(); + + unsafe { + msgctl( + id, + ControlCommands::MsgInfo as i32, + msginfo.as_mut_ptr() as *mut msqid_ds, + ); + } + + let msginfo = unsafe { msginfo.assume_init() }; + + println!("info: {:?}", msginfo); + + Ok(()) } pub struct SysvMq {