Manual page generation
This commit is contained in:
parent
b57384be97
commit
fefcf6c53d
|
@ -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"
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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(())
|
||||
}
|
|
@ -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,
|
||||
}
|
33
src/main.rs
33
src/main.rs
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue