feat: v0.3.0, daemon and continue read #2

Merged
hatter merged 1 commits from v0.3.0-dev into master 2023-09-03 13:20:13 +08:00
4 changed files with 97 additions and 32 deletions

12
Cargo.lock generated
View File

@@ -49,6 +49,15 @@ dependencies = [
"vec_map",
]
[[package]]
name = "daemonize"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
dependencies = [
"libc",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
@@ -142,9 +151,10 @@ dependencies = [
[[package]]
name = "rotate-puts"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"clap",
"daemonize",
"rust_util",
]

View File

@@ -1,6 +1,6 @@
[package]
name = "rotate-puts"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["Hatter Jiang"]
repository = "https://git.hatter.ink/hatter/rotate-puts"
@@ -11,6 +11,7 @@ categories = ["log"]
[dependencies]
clap = "2.33"
daemonize = "0.5.0"
rust_util = "0.6.41"
[profile.release]

View File

@@ -3,13 +3,22 @@
Rotate outputs from std in or assigned file
Usage:
```shell
command | rotate-puts
```
Use `mkfifo`:
```shell
mkfido test-fifo
rotate-puts --file test-fifo
command > test-fifo
```
Run as daemon:
```shell
rotate-put --file test-fifo --daemon [--continue-read]
```

View File

@@ -4,7 +4,8 @@ use std::process::exit;
use std::time::{Duration, SystemTime};
use clap::{App, AppSettings, Arg};
use rust_util::{iff, information, util_size};
use daemonize::Daemonize;
use rust_util::{failure_and_exit, iff, information, util_size};
fn main() {
let app = App::new(env!("CARGO_PKG_NAME"))
@@ -12,15 +13,21 @@ fn main() {
.about(env!("CARGO_PKG_DESCRIPTION"))
.long_about("Rotate standard in to log files")
.arg(Arg::with_name("prefix")
.long("prefix").takes_value(true).default_value("temp").help("Log file prefix"))
.long("prefix").short("P").takes_value(true).default_value("temp").help("Log file prefix"))
.arg(Arg::with_name("suffix")
.long("suffix").takes_value(true).default_value("log").help("Log file suffix"))
.long("suffix").short("S").takes_value(true).default_value("log").help("Log file suffix"))
.arg(Arg::with_name("file-size")
.long("file-size").takes_value(true).default_value("10m").help("Single log file size"))
.long("file-size").short("s").takes_value(true).default_value("10m").help("Single log file size"))
.arg(Arg::with_name("file-count")
.long("file-count").takes_value(true).default_value("10").help("Keep file count (from 0 to 1000)"))
.long("file-count").short("c").takes_value(true).default_value("10").help("Keep file count (from 0 to 1000)"))
.arg(Arg::with_name("file")
.long("file").takes_value(true).required(false).help("Read from file, default stdin"))
.long("file").short("F").takes_value(true).required(false).help("Read from file, default stdin"))
.arg(Arg::with_name("continue-read")
.long("continue-read").short("r").help("Continue read"))
.arg(Arg::with_name("ident")
.long("ident").short("i").takes_value(true).help("Identity when run in daemon mode"))
.arg(Arg::with_name("daemon")
.long("daemon").short("d").help("Run in daemon mode"))
.setting(AppSettings::ColoredHelp);
let arg_matchers = app.get_matches();
@@ -30,11 +37,38 @@ fn main() {
let file_size = util_size::parse_size(file_size).unwrap_or_else(|_| 10 * 1024 * 1028) as usize;
let file_count = arg_matchers.value_of("file-count").unwrap();
let file_count = file_count.parse().unwrap_or_else(|_| 10);
let file_count = if file_count < 0 {
0
} else if file_count > 1000 {
1000
} else { file_count as i32 };
let file_count: i32 = if file_count < 0 { 0 } else if file_count > 1000 { 1000 } else { file_count };
let continue_read = arg_matchers.is_present("continue-read");
let daemon_mode = arg_matchers.is_present("daemon");
if daemon_mode {
let ident = match arg_matchers.value_of("ident") {
Some(ident) => ident,
None => failure_and_exit!("--ident is required when running in daemon mode"),
};
let stdout = File::create(format!("/tmp/rotate-puts-daemon-{}-out.log", ident))
.expect("Create daemon out file failed");
let stderr = File::create(format!("/tmp/rotate-puts-daemon-{}-err.log", ident))
.expect("Create daemon err file failed");
let current_dir = std::env::current_dir().expect("Get current dir failed");
let daemonize = Daemonize::new()
.pid_file(&format!("/tmp/rotate-puts-daemon-{}.pid", ident))
// .chown_pid_file(true) // is optional, see `Daemonize` documentation
.working_directory(current_dir) // for default behaviour.
// .user("nobody")
// .group("daemon") // Group name
// .group(2) // or group id.
.umask(0o777) // Set umask, `0o027` by default.
.stdout(stdout) // Redirect stdout to `/tmp/daemon.out`.
.stderr(stderr) // Redirect stderr to `/tmp/daemon.err`.
.privileged_action(|| "Executed before drop privileges");
match daemonize.start() {
Ok(child) => information!("Success, daemonized: {}", child),
Err(e) => failure_and_exit!("Error, {}", e),
}
}
information!("Prefix: {}, suffix: {}, file size: {}, file count: {}",
prefix, suffix, util_size::get_display_size(file_size as i64), file_count
@@ -104,30 +138,41 @@ fn main() {
}
});
let file_opt = arg_matchers.value_of("file");
let mut read_in: Box<dyn Read> = if let Some(file) = file_opt {
let file_read = File::open(file).expect("Open file failed!");
Box::new(file_read)
} else {
Box::new(std::io::stdin())
};
let mut continue_read_count = 0;
information!("Continue read: {}", continue_read);
'read_open_loop: while continue_read || (continue_read_count == 0) {
if continue_read_count > 0 { information!("Continue read at #{}", continue_read_count); }
continue_read_count += 1;
let file_opt = arg_matchers.value_of("file");
let mut read_in: Box<dyn Read> = if let Some(file) = file_opt {
let file_read = File::open(file).expect("Open file failed!");
Box::new(file_read)
} else {
Box::new(std::io::stdin())
};
let mut buff = [0_u8; 128];
loop {
match read_in.read(&mut buff) {
Ok(len) if len == 0 => {
sender.send(Vec::new()).ok();
}
Ok(len) => {
let mut vec = Vec::with_capacity(len);
vec.extend_from_slice(&buff[0..len]);
if let Err(e) = sender.send(vec) {
eprintln!("[ERROR] Send error: {}", e);
let mut buff = [0_u8; 128];
loop {
match read_in.read(&mut buff) {
Ok(len) if len == 0 => {
if continue_read {
continue 'read_open_loop; // continue and reopen file or stdin
} else {
sender.send(Vec::new()).ok();
}
}
Ok(len) => {
let mut vec = Vec::with_capacity(len);
vec.extend_from_slice(&buff[0..len]);
if let Err(e) = sender.send(vec) {
eprintln!("[ERROR] Send error: {}", e);
}
}
Err(e) => eprintln!("[ERROR] Send error: {}", e),
}
Err(e) => eprintln!("[ERROR] Send error: {}", e),
}
}
information!("End rotate-puts")
}
fn make_new_file_name(prefix: &str, suffix: &str, file_count: i32, index: &mut i32) -> String {