Manual page generation

This commit is contained in:
Pranav Karawale 2022-10-12 12:25:28 +05:30
parent b57384be97
commit fefcf6c53d
Signed by: pranav
GPG Key ID: 1A551FFE981F09AB
5 changed files with 93 additions and 22 deletions

17
Cargo.lock generated
View File

@ -74,6 +74,16 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "clap_mangen"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d5cd261a1d5601621a7ee4870f6e7f3f1ba3fc901d867f5201b36691e7efbe"
dependencies = [
"clap",
"roff",
]
[[package]]
name = "heck"
version = "0.4.0"
@ -94,6 +104,7 @@ name = "ipcsockd"
version = "0.1.0"
dependencies = [
"clap",
"clap_mangen",
"signal-hook",
"tokio",
]
@ -243,6 +254,12 @@ dependencies = [
"bitflags",
]
[[package]]
name = "roff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]]
name = "scopeguard"
version = "1.1.0"

View File

@ -1,5 +1,7 @@
[package]
name = "ipcsockd"
description = "A super-server daemon for UNIX domain sockets"
authors = ["Pranav Karawale <https://karawale.in>"]
version = "0.1.0"
edition = "2021"
@ -9,8 +11,13 @@ edition = "2021"
opt-level = "s"
lto = true
codegen-units = 1
strip = true
[dependencies]
clap = { version = "4.0.11", features = ["derive"] }
clap = { version = "4", features = ["cargo", "derive"] }
signal-hook = "*"
tokio = { version = "1", features = ["full"] }
[build-dependencies]
clap_mangen = "0.2"
clap = { version = "4", features = ["cargo", "derive"] }

18
build.rs Normal file
View File

@ -0,0 +1,18 @@
use clap_mangen::Man;
include!("src/cli.rs");
fn main() -> std::io::Result<()> {
let out_dir = std::path::PathBuf::from(
std::env::var_os("OUT_DIR").ok_or_else(|| std::io::ErrorKind::NotFound)?,
);
let cmd = Cli::command();
let man = Man::new(cmd);
let mut buffer: Vec<u8> = Default::default();
man.render(&mut buffer)?;
std::fs::write(out_dir.join("ipcsockd.1"), buffer)?;
Ok(())
}

38
src/cli.rs Normal file
View File

@ -0,0 +1,38 @@
#[allow(unused_imports)]
use clap::{CommandFactory, Parser};
/// A super-server daemon for UNIX domain sockets.
///
/// ipcsockd (inter process communication socket daemon) binds to a UNIX domain
/// socket, spawns the given command for each connection in a thread with the
/// standard input and standard output of the thread piped to the socket.
///
/// If the command to be executed contains flags of its own, they might conflict
/// with the flags passed to ipcsockd itself. To work around this issue, pass '--'
/// before the actual command.
///
/// EXAMPLE: ipcsockd ./a.sock -- mycommand --arg1 --arg2 yes
///
/// Here, the arguments --arg1 and --arg2 are passed to mycommand during the time
/// of execution instead of being parsed by ipcsockd.
///
/// ipcsockd prints a message \"OK\" to standard error when it is ready to receive
/// requests. This can be utilised to check the status of the server.
///
/// ipcsockd limits the maximum amount of concurrent requests at a particular point
/// in time, which can be configured using flags. Advanced rate-limiting and similar
/// utilities are left to be handled by a reverse proxy.
#[derive(Parser, Debug)]
#[command(author, version, about, verbatim_doc_comment)]
pub struct Cli {
/// Path of the UNIX socket to listen on.
pub path: String,
/// Command with arguments to execute for every incoming connection.
pub cmd: Vec<String>,
/// Maximum concurrent connections per second.
#[arg(short, long, default_value_t = 40u32)]
pub limit: u32,
}

View File

@ -2,6 +2,8 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
pub mod cli;
use clap::Parser;
use signal_hook::consts::TERM_SIGNALS;
use signal_hook::iterator::Signals;
@ -11,28 +13,14 @@ use std::path::Path;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use tokio::net::UnixListener;
use tokio::task;
use tokio::{fs, process};
/// A super-server daemon for UNIX domain sockets.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Path of the UNIX socket to listen on.
path: String,
/// Command with arguments to execute for every incoming connection.
cmd: Vec<String>,
/// Maximum concurrent connections per second.
#[arg(short, long, default_value_t = 40u32)]
limit: u32,
}
static START_MESSAGE: &str = "OK";
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let cli = Cli::parse();
let cli = cli::Cli::parse();
let limit = cli.limit.clone();
if Path::new(cli.path.as_str()).exists() {
@ -42,7 +30,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let listener = UnixListener::bind(cli.path.clone())?;
let thread_count = Arc::new(AtomicU32::new(0));
tokio::spawn(async move {
let mainloop = tokio::spawn(async move {
eprintln!("{}", START_MESSAGE);
loop {
if thread_count.load(Ordering::Relaxed) < limit {
@ -54,7 +42,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let cmd = cli.cmd.clone();
thread_count.fetch_add(1, Ordering::Relaxed);
tokio::spawn(async move {
task::spawn(async move {
handle_conn(cmd, conn_as_fd, thread_count).await;
})
.await
@ -67,19 +55,22 @@ async fn main() -> Result<(), Box<dyn Error>> {
for _ in signals.forever() {
fs::remove_file(cli.path.clone()).await?;
// mainloop will probably abort on its own when its dropped, but better
// be safe.
mainloop.abort();
break;
}
Ok(())
}
async fn handle_conn(sock_cmd: Vec<String>, conn_as_fd: OwnedFd, count: Arc<AtomicU32>) {
let (_, cmd_args) = sock_cmd.as_slice().split_first().unwrap();
async fn handle_conn(cmd: Vec<String>, conn_as_fd: OwnedFd, count: Arc<AtomicU32>) {
let (_, cmd_args) = cmd.as_slice().split_first().unwrap();
let stdin = conn_as_fd.try_clone().unwrap();
let stdout = conn_as_fd.try_clone().unwrap();
let mut handle = process::Command::new(sock_cmd.first().to_owned().unwrap())
let mut handle = process::Command::new(cmd.first().to_owned().unwrap())
.args(cmd_args.to_owned())
.stdin(stdin)
.stdout(stdout)