From e5962ca5439509c8a0151b48b9a254dc32decd83 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 3 Sep 2023 13:18:59 +0800 Subject: [PATCH] feat: v0.3.0, daemon and continue read --- Cargo.lock | 12 +++++- Cargo.toml | 3 +- README.md | 9 +++++ src/main.rs | 105 +++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e661f2..c5f97e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/Cargo.toml b/Cargo.toml index 5b95aba..c1031d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/README.md b/README.md index 5bf303b..a5fc3fe 100644 --- a/README.md +++ b/README.md @@ -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] +``` + diff --git a/src/main.rs b/src/main.rs index 8588536..b4d2c26 100644 --- a/src/main.rs +++ b/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 = 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 = 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 { -- 2.27.0