mirror of
https://github.com/zaphar/clio.git
synced 2025-07-28 06:59:49 -04:00
Compare commits
3 Commits
0b0fe7df8e
...
be80b940df
Author | SHA1 | Date | |
---|---|---|---|
be80b940df | |||
8e584c9e61 | |||
20adbc02fb |
@ -9,5 +9,5 @@ description = "A small log redirection utility"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
nix = { version = "0.29.0", features = ["signal", "process"] }
|
nix = { version = "0.29.0", features = ["signal", "process", "fs"] }
|
||||||
tokio = { version = "1.38.0", features = ["process", "signal", "rt", "rt-multi-thread", "macros", "io-util", "fs"] }
|
tokio = { version = "1.38.0", features = ["process", "signal", "rt", "rt-multi-thread", "macros", "io-util", "fs", "time"] }
|
||||||
|
50
src/main.rs
50
src/main.rs
@ -1,4 +1,5 @@
|
|||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{ExitCode, ExitStatus, Stdio};
|
use std::process::{ExitCode, ExitStatus, Stdio};
|
||||||
|
|
||||||
@ -42,6 +43,12 @@ struct Args {
|
|||||||
pid_file: Option<PathBuf>,
|
pid_file: Option<PathBuf>,
|
||||||
#[arg(long = "sig", value_enum, help="Signal notifiying that the file paths have been rotated", default_value_t = HandledSignals::SIGHUP)]
|
#[arg(long = "sig", value_enum, help="Signal notifiying that the file paths have been rotated", default_value_t = HandledSignals::SIGHUP)]
|
||||||
rotated_signal: HandledSignals,
|
rotated_signal: HandledSignals,
|
||||||
|
#[arg(
|
||||||
|
long = "paranoid",
|
||||||
|
help = "Be paranoid about the open file descriptors",
|
||||||
|
default_value_t = false
|
||||||
|
)]
|
||||||
|
paranoid: bool,
|
||||||
#[arg(last = true, help = "Command to run")]
|
#[arg(last = true, help = "Command to run")]
|
||||||
cmd: Vec<String>,
|
cmd: Vec<String>,
|
||||||
}
|
}
|
||||||
@ -66,6 +73,19 @@ async fn cleanup(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_for_stale_handle(f: &File, path: &PathBuf) -> anyhow::Result<bool> {
|
||||||
|
let fd = f.as_raw_fd();
|
||||||
|
let stats = match nix::sys::stat::fstat(fd) {
|
||||||
|
Ok(st) => st,
|
||||||
|
Err(_) => return Ok(false),
|
||||||
|
};
|
||||||
|
let checkstats = match nix::sys::stat::stat(path) {
|
||||||
|
Ok(st) => st,
|
||||||
|
Err(_) => return Ok(false),
|
||||||
|
};
|
||||||
|
return Ok(stats.st_nlink > 0 && stats.st_ino == checkstats.st_ino);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<ExitCode> {
|
async fn main() -> anyhow::Result<ExitCode> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
@ -112,18 +132,32 @@ async fn main() -> anyhow::Result<ExitCode> {
|
|||||||
if let Some(p) = &args.pid_file {
|
if let Some(p) = &args.pid_file {
|
||||||
write_pid_file(p).await?
|
write_pid_file(p).await?
|
||||||
}
|
}
|
||||||
// TODO(jwall): Forward all other signals to the running process.
|
let mut check_interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// NOTE(zaphar): Each select block will run exclusively of the other blocks using a
|
// NOTE(zaphar): Each select block will run exclusively of the other blocks using a
|
||||||
// psuedorandom order.
|
// psuedorandom order.
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
// wait for a read on stdout
|
// wait for a read on stdout
|
||||||
|
_ = check_interval.tick() => {
|
||||||
|
if args.paranoid {
|
||||||
|
if !check_for_stale_handle(&stdout_writer, stdout_path)? {
|
||||||
|
eprintln!("Detected closed stdout handle");
|
||||||
|
stdout_writer.flush().await?;
|
||||||
|
stdout_writer = File::options().append(true).create(true).open(stdout_path).await?;
|
||||||
|
}
|
||||||
|
if !check_for_stale_handle(&stderr_writer, stderr_path)? {
|
||||||
|
eprintln!("Detected closed stderr handle");
|
||||||
|
stderr_writer.flush().await?;
|
||||||
|
stderr_writer = File::options().append(true).create(true).open(stderr_path).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
out_result = stdout_reader.read(&mut stdout_buffer) => {
|
out_result = stdout_reader.read(&mut stdout_buffer) => {
|
||||||
match out_result {
|
match out_result {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
// TODO(zaphar): It is possible we should try to reopen the file if this
|
|
||||||
// write fails in some cases.
|
|
||||||
if let Err(_) = stdout_writer.write(&stdout_buffer[0..n]).await {
|
if let Err(_) = stdout_writer.write(&stdout_buffer[0..n]).await {
|
||||||
|
stdout_writer.flush().await?;
|
||||||
stdout_writer = File::options().append(true).create(true).open(stdout_path).await?;
|
stdout_writer = File::options().append(true).create(true).open(stdout_path).await?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -139,9 +173,8 @@ async fn main() -> anyhow::Result<ExitCode> {
|
|||||||
err_result = stderr_reader.read(&mut stderr_buffer) => {
|
err_result = stderr_reader.read(&mut stderr_buffer) => {
|
||||||
match err_result {
|
match err_result {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
// TODO(zaphar): It is possible we should try to reopen the file if this
|
|
||||||
// write fails in some cases.
|
|
||||||
if let Err(_) = stderr_writer.write(&stderr_buffer[0..n]).await {
|
if let Err(_) = stderr_writer.write(&stderr_buffer[0..n]).await {
|
||||||
|
stderr_writer.flush().await?;
|
||||||
stderr_writer = File::options().append(true).create(true).open(stderr_path).await?;
|
stderr_writer = File::options().append(true).create(true).open(stderr_path).await?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -154,17 +187,18 @@ async fn main() -> anyhow::Result<ExitCode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = rotation_signal_stream.recv() => {
|
_ = rotation_signal_stream.recv() => {
|
||||||
// on sighub sync and reopen our files
|
// on sighup sync and reopen our files
|
||||||
// NOTE(zaphar): This will cause the previously opened handles to get
|
// NOTE(zaphar): This will cause the previously opened handles to get
|
||||||
// dropped which will cause them to close assuming all the io has finished. This is why we sync
|
// dropped which will cause them to close assuming all the io has finished. This is why we sync
|
||||||
// before reopening the files.
|
// before reopening the files.
|
||||||
// TODO(zaphar): These should do something in the event of an error
|
eprintln!("got rotation signal");
|
||||||
_ = stderr_writer.sync_all().await;
|
_ = stderr_writer.sync_all().await;
|
||||||
_ = stdout_writer.sync_all().await;
|
_ = stdout_writer.sync_all().await;
|
||||||
stderr_writer = File::options().append(true).create(true).open(stderr_path).await?;
|
stderr_writer = File::options().append(true).create(true).open(stderr_path).await?;
|
||||||
stdout_writer = File::options().append(true).create(true).open(stdout_path).await?;
|
stdout_writer = File::options().append(true).create(true).open(stdout_path).await?;
|
||||||
}
|
}
|
||||||
_ = sigterm_stream.recv() => {
|
_ = sigterm_stream.recv() => {
|
||||||
|
eprintln!("got sigterm signal");
|
||||||
// NOTE(zaphar): This is a giant hack.
|
// NOTE(zaphar): This is a giant hack.
|
||||||
// If https://github.com/tokio-rs/tokio/issues/3379 ever get's implemented it will become
|
// If https://github.com/tokio-rs/tokio/issues/3379 ever get's implemented it will become
|
||||||
// unnecessary.
|
// unnecessary.
|
||||||
@ -180,6 +214,7 @@ async fn main() -> anyhow::Result<ExitCode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = sigquit_stream.recv() => {
|
_ = sigquit_stream.recv() => {
|
||||||
|
eprintln!("got sigquit signal");
|
||||||
// NOTE(zaphar): This is a giant hack.
|
// NOTE(zaphar): This is a giant hack.
|
||||||
// If https://github.com/tokio-rs/tokio/issues/3379 ever get's implemented it will become
|
// If https://github.com/tokio-rs/tokio/issues/3379 ever get's implemented it will become
|
||||||
// unnecessary.
|
// unnecessary.
|
||||||
@ -195,6 +230,7 @@ async fn main() -> anyhow::Result<ExitCode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = sigint_stream.recv() => {
|
_ = sigint_stream.recv() => {
|
||||||
|
eprintln!("got sigint signal");
|
||||||
// NOTE(zaphar): This is a giant hack.
|
// NOTE(zaphar): This is a giant hack.
|
||||||
// If https://github.com/tokio-rs/tokio/issues/3379 ever get's implemented it will become
|
// If https://github.com/tokio-rs/tokio/issues/3379 ever get's implemented it will become
|
||||||
// unnecessary.
|
// unnecessary.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user