feat: v0.3.0, daemon and continue read #2
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
|
||||
105
src/main.rs
105
src/main.rs
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user