tests and doc

This commit is contained in:
2025-12-31 12:40:31 +11:00
parent 29cc80f405
commit 3af564d826
4 changed files with 107 additions and 15 deletions
Generated
+10
View File
@@ -24,6 +24,15 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "fork"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29714eb48a54d35d6f107e38cc58003f2e26825f222119db8369d28b24b79f2a"
dependencies = [
"libc",
]
[[package]]
name = "funty"
version = "2.0.0"
@@ -67,6 +76,7 @@ name = "signalstream"
version = "0.1.0"
dependencies = [
"bitvec",
"fork",
"libc",
"signal-hook",
]
+3
View File
@@ -7,3 +7,6 @@ edition = "2024"
bitvec = "1.0.1"
libc = "0.2.178"
signal-hook = "0.4.1"
[dev-dependencies]
fork = "0.6.0"
+91 -15
View File
@@ -1,34 +1,91 @@
#![warn(missing_docs)]
//! Send data over UNIX signals.
//!
//! UNIX provides signals, like SIGTERM and SIGSTOP which let two processes, or the kernel and the
//! process commmunicate with each other. This form of IPC can be used to send arbitrary binary
//! data, if both processes cooperate.
//!
//! **Please do not use this in production**. There are many better ways to send data between
//! processes, like [pipes](https://man7.org/linux/man-pages/man2/pipe.2.html)
//!
//! # Usage
//! Use of this crate requires initialisation of the [`SignalStream`] object. This object can be used
//! to write or read from the SignalStream of the other specified process.
//! `read()` must always be called by a recieving process before any data is sent over the
//! SignalStream, otherwise data may be incomplete or corrupted. See the documentation for `read()`
//! for more details.
//!
//! ```rust
//! // Process 1
//! # use std::str::*;
//! # let pid2 = 0;
//! use signalstream::*;
//! let mut sigstream = SignalStream::new(pid2);
//! let mut buf = [0; 6];
//! sigstream.read(&mut buf);
//! println!("String: {}", from_utf8(&buf).unwrap());
//!
//! // Process 2
//! # let pid1 = 0;
//! let mut sigstream = SignalStream::new(pid1);
//! sigstream.write("Hello!".as_bytes());
//! ```
//! Process 2 must begin execution after Process 1.
//!
//! # Notes
//! This crate uses the `SIGUSR1`, `SIGUSR2` and `SIGSTKFLT` signals to transfer data and indicate
//! EOF. Though unikely, if your program (or the other program) use these signals, unexpected
//! behavior may occur. Additionally, if the other program does not use SignalStream, recieving
//! these signals may cause it to terminate.
use bitvec::prelude::*;
use std::io::{self, Read, Write};
use libc::SIGSTKFLT;
use std::{io, thread::sleep, time::Duration};
use signal_hook::consts::*;
use signal_hook::iterator::Signals;
/// Initialising a signalstream object begins the
pub struct SignalStream {
signals: Signals,
pid: u32,
}
pub use std::io::{Read, Write};
impl SignalStream {
/// Constructor for a SignalStream.
///
/// Accepts the PID of a the process to communicate with.
pub fn new(pid: u32) -> Self {
Self {
signals: Signals::new([SIGUSR1, SIGUSR2]).unwrap(),
signals: Signals::new([SIGUSR1, SIGUSR2, SIGSTKFLT]).unwrap(),
pid: pid,
}
}
}
impl Read for SignalStream {
impl std::io::Read for SignalStream {
/// `read()` tries to read `buf.len()` bytes from the SignalStream. It returns when the
/// buffer is full or it recieves an end of file signal - otherwise, it blocks indefinitely.
///
/// Its return value is the number of bytes read. It may be less than the number of bytes if
/// the writing process finishes sending data early.
///
/// Due to limitations in the `signal_hook` crate, this function must be called and be
/// blocking before any signals are sent from the other process.
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut n = 0;
let pending = self.signals.forever();
let forever = self.signals.forever();
let mut bitlist = bitvec![];
for signal in pending {
for signal in forever {
n += 1;
bitlist.push(match signal {
SIGUSR1 => false,
SIGUSR2 => true,
SIGSTKFLT => break, // SIGSTKFLT is the end of file signal.
_ => {
// This should be impossible, as only the SIGUSR1 and SIGUSR2 signals are
// This should be impossible, as only the SIGUSR1, SIGUSR2 and SIGSTKFLT signals are
// registered to begin with.
panic!("Program recieved an unexpected signal, despite it not being registered")
}
@@ -44,14 +101,14 @@ impl Read for SignalStream {
for (i, bit) in bitlist.iter().enumerate() {
let byte = i / 8;
let shift = 7 - i % 8;
buf[byte] |= ((*bit) as u8) << shift
buf[byte] |= ((*bit) as u8) << shift;
}
Ok(n)
}
}
impl Write for SignalStream {
impl std::io::Write for SignalStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
for num in buf {
let bit_representation = String::from(format!("{num:08b}"));
@@ -64,28 +121,47 @@ impl Write for SignalStream {
1 => SIGUSR2,
_ => panic!("Non-binary digit in binary representation"),
};
unsafe {
libc::kill(self.pid as i32, signal);
sleep(Duration::from_millis(10));
};
}
}
};
unsafe { libc::kill(self.pid as i32, SIGSTKFLT) };
Ok(0)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
todo!()
}
}
#[cfg(test)]
mod tests {
use std::{thread::sleep, time::Duration};
use libc::fork;
use super::*;
use std::{process::Command, thread::sleep, time::Duration};
const LIPSUM: &'static str = include_str!("lipsum.txt");
#[test]
fn read_test() {}
fn test() {
let new_pid = unsafe {
fork() as u32
};
#[test]
fn write_test() {}
if new_pid == 0 { // Reader process, as the process of the forked program is unknown
let mut sigstream = SignalStream::new(0);
let mut buf = [0; LIPSUM.len()];
sigstream.read(&mut buf).unwrap();
assert_eq!(buf, LIPSUM.as_bytes());
} else { // Writing process, as it knows the pid of the original program
let mut sigstream = SignalStream::new(new_pid);
sleep(Duration::from_millis(10));
sigstream.write(LIPSUM.as_bytes()).unwrap();
dbg!("pong");
};
}
}
+3
View File
@@ -0,0 +1,3 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean at eros ac arcu elementum suscipit vel eu dolor. Morbi vel tristique leo, ut vehicula lacus. Phasellus commodo ullamcorper ex, non placerat orci mattis eu. In ac ex a leo pharetra elementum. Duis dapibus, dui commodo bibendum consectetur, dui quam mattis sapien, eu ullamcorper ipsum sapien in lacus. Cras fermentum eget arcu ut tincidunt. Donec ut mi vitae nibh imperdiet tempor a eu ipsum. Sed rutrum, elit sed rhoncus posuere, dolor eros elementum turpis, sit amet ullamcorper magna nulla eu mi. Donec in scelerisque orci. In facilisis, est eu scelerisque rhoncus, risus massa rutrum enim, quis ultrices libero tellus ut nibh. Sed vel aliquam dui. Integer et molestie nulla.
Sed eros turpis, aliquet non risus ut, cursus faucibus erat. Sed ac aliquam ex. Sed vehicula magna id velit lobortis congue. Fusce placerat blandit urna id fermentum. Integer quis vestibulum ex, vel mattis quam. Etiam pellentesque sed lacus eu volutpat. Duis luctus consectetur urna at malesuada. Cras aliquet interdum vulputate. In commodo ornare urna, sit amet fringilla nunc. Praesent dolor nulla, suscipit non elementum sit amet, cursus in lectus. Vestibulum ultrices pulvinar tempus.