diff --git a/Cargo.lock b/Cargo.lock index 5297471..b9b4c04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -263,7 +263,7 @@ dependencies = [ [[package]] name = "drcom4scut" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bytes", "chrono", @@ -273,7 +273,6 @@ dependencies = [ "encoding_rs", "hex", "hostname", - "lazy_static", "log 0.4.11", "log4rs", "md-5", diff --git a/Cargo.toml b/Cargo.toml index 92ac93c..2bb84f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "drcom4scut" -version = "0.2.0" +version = "0.2.1" authors = ["SeaLoong <984391132@qq.com>"] edition = "2018" +description = "A 3rd-party Drcom client for SCUT." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] rand = "0.7.3" -lazy_static = "1.4.0" log = "0.4.11" log4rs = "0.13.0" chrono = "0.4" @@ -26,4 +26,7 @@ crossbeam = "0.7.3" [profile.release] opt-level = 'z' codegen-units = 1 -lto = true \ No newline at end of file +lto = true + +[features] +nolog = [] \ No newline at end of file diff --git a/README.md b/README.md index 1b3ff2b..7a4b6ab 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # drcom4scut -> 当前版本 0.2.0 +> 当前版本 0.2.1 -+ A 3rd DrCOM client for SCUT, written by Rust. ++ A 3rd-party DrCOM client for SCUT, written in Rust. + 华南理工大学第三方客户端,使用Rust语言编写。 ------------------------------------------------------ @@ -31,6 +31,7 @@ USAGE: FLAGS: --debug Enable debug mode. -h, --help Prints help information + --nolog !!!注意:如果构建时启用了nolog特性则不会有这项!!! Disable logger, no any output at all, unless PANIC or EXCEPTION of program occurred. --noudp Disable UDP Process. -V, --version Prints version information @@ -73,7 +74,9 @@ retry: count: 2 # (可选) 错误重试次数 interval: 5000 # (可选) 数据包重发间隔、错误重试间隔 log: - directory: ./logs # (可选) 日志目录 + enable_console: false # (可选) 是否输出日志到控制台 + enable_file: false # (可选) 是否输出日志到文件 + file_directory: ./logs # (可选) 日志文件目录 level: INFO # (可选) 日志等级 data: # (可选) 以下参数通常不需要填写,填写错误可能会导致不可预计的问题 response_identity: @@ -102,6 +105,11 @@ data: # (可选) 以下参数通常不需要填写,填写错误可能会导 cargo build --release ``` ++ 如果你想去掉程序的日志(log)功能,可以启用 `nolog` 特性 + ```bash + cargo build --release --features nolog + ``` + + 需要使用 *Nightly* 版本的 Rust 进行编译。 + 由于使用了 [**libpnet**](https://crates.io/crates/pnet) ,在Windows下需要安装 *WinPcap* 或 *pcap* 才能进行编译,详见[**libpnet**](https://crates.io/crates/pnet)。 diff --git a/src/eap.rs b/src/eap.rs index 285b7bf..068177e 100644 --- a/src/eap.rs +++ b/src/eap.rs @@ -1,7 +1,10 @@ use std::sync::Arc; use std::time::Duration; +#[cfg(feature = "nolog")] +use crate::{debug, error, info, log, trace, warn}; use bytes::{Buf, BufMut, Bytes, BytesMut}; +#[cfg(not(feature = "nolog"))] use log::{debug, error, info, warn}; use md5::Digest; use pnet::datalink::MacAddr; @@ -10,7 +13,7 @@ use crate::constants; use crate::device::Device; use crate::eap::packet::*; use crate::settings::Settings; -use crate::util::*; +use crate::util::{ip_to_vec, sleep, ChannelData}; use chrono::Local; use crossbeam::{Receiver, Sender, TryRecvError}; use std::str::FromStr; @@ -644,3 +647,8 @@ fn test_md5_calc() { let r = hex::encode(md5::Md5::digest(data.bytes())).to_lowercase(); assert_eq!(&r, "313a3758ad589ce03dc6af0371c31239"); } + +#[test] +fn test_log() { + debug!("debug test"); +} diff --git a/src/main.rs b/src/main.rs index 6eb381f..aa0a5b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,34 @@ -// #![allow(unused_must_use, dead_code, unused_variables, unused_imports)] +#![allow(unused_must_use, dead_code, unused_variables, unused_imports)] #![feature(ip)] -extern crate lazy_static; +mod constants; +mod device; +mod eap; +mod settings; +mod socket; +mod udp; +#[macro_use] +mod util; use std::str::FromStr; use std::sync::Arc; use std::thread; use std::time::Duration; -use clap::clap_app; -use log::LevelFilter::{Debug, Info}; -use log::{error, info, LevelFilter}; - use crate::settings::Settings; use crate::socket::Socket; use crate::util::{sleep_at, ChannelData}; +use clap::{clap_app, ArgMatches}; +#[cfg(not(feature = "nolog"))] +use log::{ + debug, error, info, trace, warn, LevelFilter, + LevelFilter::{Debug, Info}, +}; -mod constants; -mod device; -mod eap; -mod settings; -mod socket; -mod udp; -mod util; - -fn init_logger(settings: &Settings) -> log4rs::Handle { +#[cfg(not(feature = "nolog"))] +fn init_logger(settings: &Settings) { + if !settings.debug && settings.nolog { + return; + } use log4rs::{ append::{ console::ConsoleAppender, @@ -38,30 +43,45 @@ fn init_logger(settings: &Settings) -> log4rs::Handle { config::{Appender, Config, Root}, encode::pattern::PatternEncoder, }; - let directory = &settings.log.directory; - let stdout = ConsoleAppender::builder() - .encoder(Box::new(PatternEncoder::new( - "{h([{d(%Y-%m-%d %H:%M:%S)}][{l}][{T}] {m}{n})}", - ))) - .build(); - - let logfile = RollingFileAppender::builder() - .encoder(Box::new(PatternEncoder::new( - "[{d(%Y-%m-%d %H:%M:%S)}][{l}][{T}][{M}:{L}] {m}{n}", - ))) - .build( - directory.clone() + "/latest.log", - Box::new(CompoundPolicy::new( - Box::new(SizeTrigger::new(1 << 20)), - Box::new( - FixedWindowRoller::builder() - .base(1) - .build(&(directory.clone() + "/log-{}.gz"), 10) - .unwrap(), - ), - )), + let stdout = if settings.debug || settings.log.enable_console { + Some( + ConsoleAppender::builder() + .encoder(Box::new(PatternEncoder::new( + "{h([{d(%Y-%m-%d %H:%M:%S)}][{l}][{T}] {m}{n})}", + ))) + .build(), ) - .unwrap(); + } else { + None + }; + let logfile = if settings.log.enable_file { + let directory = &settings.log.file_directory; + Some( + RollingFileAppender::builder() + .encoder(Box::new(PatternEncoder::new( + "[{d(%Y-%m-%d %H:%M:%S)}][{l}][{T}][{M}:{L}] {m}{n}", + ))) + .build( + directory.clone() + "/latest.log", + Box::new(CompoundPolicy::new( + Box::new(SizeTrigger::new(1 << 22)), + Box::new( + FixedWindowRoller::builder() + .base(1) + .build(&(directory.clone() + "/log-{}.gz"), 10) + .expect("Can't build FixedWindowRoller!"), + ), + )), + ) + .expect("Can't build RollingFileAppender!"), + ) + } else { + None + }; + + if stdout.is_none() && logfile.is_none() { + return; + } let level = if settings.debug { Debug @@ -69,22 +89,27 @@ fn init_logger(settings: &Settings) -> log4rs::Handle { LevelFilter::from_str(&*settings.log.level).unwrap_or(Info) }; - let config = Config::builder() - .appender(Appender::builder().build("stdout", Box::new(stdout))) - .appender(Appender::builder().build("logfile", Box::new(logfile))) - .build( - Root::builder() - .appender("stdout") - .appender("logfile") - .build(level), - ) - .unwrap(); + let mut config = Config::builder(); + let mut root = Root::builder(); + if let Some(stdout) = stdout { + config = config.appender(Appender::builder().build("stdout", Box::new(stdout))); + root = root.appender("stdout"); + } + if let Some(logfile) = logfile { + config = config.appender(Appender::builder().build("logfile", Box::new(logfile))); + root = root.appender("logfile"); + } - log4rs::init_config(config).unwrap() + let config = config + .build(root.build(level)) + .expect("Can't build log config!"); + + log4rs::init_config(config).expect("Can't init log config!"); } #[test] fn test_logger() { + #[cfg(not(feature = "nolog"))] init_logger(&Settings::default()); trace!("trace test"); debug!("debug test"); @@ -93,11 +118,12 @@ fn test_logger() { error!("error test"); } -fn main() { - let matches = clap_app!(drcom4scut => - (version: "0.1.0") - (author: "SeaLoong") - (about: "A 3rd-party Drcom client for SCUT.") +fn get_matches<'a>() -> ArgMatches<'a> { + use clap::*; + let app = clap_app!((crate_name!()) => + (version: crate_version!()) + (author: crate_authors!()) + (about: crate_description!()) (@arg debug: --debug "Enable debug mode.") (@arg config: -c --config +takes_value "(Optional) Path to config file. Some settings only can be set by config file.") (@arg mac: -m --mac +takes_value "(Optional) Ethernet Device MAC address.") @@ -109,14 +135,24 @@ fn main() { (@arg hostname: -N --hostname +takes_value "(Optional) Default value is current computer host name.") (@arg time: -t --time +takes_value "(Optional) Time to reconnect automatically after you are not allowed to access Internet. Default value is 7:00.") (@arg noudp: --noudp "Disable UDP Process.") - ) - .get_matches(); + ); + #[cfg(not(feature = "nolog"))] + let app = app.arg(clap::Arg::with_name("nolog").long("nolog").help( + "Disable logger, no any output at all, unless PANIC or EXCEPTION of program occurred.", + )); + app.get_matches() +} + +fn main() { + let matches = get_matches(); let (mut settings, cfg) = settings::Settings::new(&matches).expect("Can't read config file."); - init_logger(&settings); settings.done(matches, cfg); + #[cfg(not(feature = "nolog"))] + init_logger(&settings); + info!("Start to run..."); let device = device::get_device(settings.mac, settings.ip).expect("Fail on getting ethernet device!"); @@ -131,7 +167,7 @@ fn main() { info!("Host: {}", settings.host); info!("Hostname: {}", settings.hostname); info!("Time to wake up: {}", settings.time); - info!("Reconnect Seconds: {}s", settings.reconnect); + info!("Reconnect Interval: {}s", settings.reconnect); info!( "Heartbeat timeout of EAP: {}s", settings.heartbeat.eap_timeout @@ -142,7 +178,9 @@ fn main() { ); info!("Retry Count: {}", settings.retry.count); info!("Retry Interval: {}ms", settings.retry.interval); - info!("Log Directory: {}", settings.log.directory); + info!("Log to console: {}", settings.log.enable_console); + info!("Log to file: {}", settings.log.enable_file); + info!("Log File Directory: {}", settings.log.file_directory); info!("Log Level: {}", settings.log.level); let settings = Arc::new(settings); @@ -224,8 +262,8 @@ fn main() { } }) .expect("Can't create EAP Process generator thread!"); - if settings.no_udp { - info!("UDP Process is disabled.") + if settings.noudp { + info!("UDP Process is disabled."); } else { let tx = tx.clone(); loop { diff --git a/src/settings.rs b/src/settings.rs index 662a5a2..427dcc0 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; +#[cfg(feature = "nolog")] +use crate::{debug, error, info, log, trace, warn}; use chrono::NaiveTime; use config::{Config, FileFormat, Value}; +#[cfg(not(feature = "nolog"))] use log::error; use pnet::datalink::MacAddr; use std::cmp::max; @@ -12,7 +15,8 @@ use std::str::FromStr; #[derive(Debug, Hash, PartialEq, Eq, Clone)] pub struct Settings { pub debug: bool, - pub no_udp: bool, + pub noudp: bool, + pub nolog: bool, pub path: String, pub mac: Option, pub ip: Option, @@ -25,6 +29,7 @@ pub struct Settings { pub reconnect: i32, pub heartbeat: Heartbeat, pub retry: Retry, + #[cfg(not(feature = "nolog"))] pub log: Log, pub data: Data, } @@ -33,7 +38,8 @@ impl Default for Settings { fn default() -> Self { Settings { debug: false, - no_udp: false, + noudp: false, + nolog: false, path: String::from("config.yml"), mac: None, ip: None, @@ -46,6 +52,7 @@ impl Default for Settings { reconnect: 120, heartbeat: Heartbeat::default(), retry: Retry::default(), + #[cfg(not(feature = "nolog"))] log: Log::default(), data: Data::default(), } @@ -97,16 +104,22 @@ impl Default for Retry { } } +#[cfg(not(feature = "nolog"))] #[derive(Debug, Hash, PartialEq, Eq, Clone)] pub struct Log { - pub directory: String, + pub enable_console: bool, + pub enable_file: bool, + pub file_directory: String, pub level: String, } +#[cfg(not(feature = "nolog"))] impl Default for Log { fn default() -> Self { Log { - directory: String::from("./logs"), + enable_console: true, + enable_file: true, + file_directory: String::from("./logs"), level: String::from("INFO"), } } @@ -182,6 +195,10 @@ fn get_int_from_map(map: &HashMap, k: &str) -> Option { map.get(k).and_then(|v| v.to_owned().into_int().ok()) } +fn get_bool_from_map(map: &HashMap, k: &str) -> Option { + map.get(k).and_then(|v| v.to_owned().into_bool().ok()) +} + fn get_map_from_map(map: &HashMap, k: &str) -> Option> { map.get(k).and_then(|v| v.to_owned().into_table().ok()) } @@ -209,7 +226,8 @@ impl Settings { pub fn new(matches: &clap::ArgMatches) -> Result<(Settings, Config), config::ConfigError> { let mut settings = Settings::default(); settings.debug = matches.is_present("debug"); - settings.no_udp = matches.is_present("noudp"); + settings.noudp = matches.is_present("noudp"); + settings.nolog = matches.is_present("nolog"); let path = Path::new( matches @@ -303,9 +321,16 @@ impl Settings { } } + #[cfg(not(feature = "nolog"))] if let Ok(map) = cfg.get_table("log") { - if let Some(x) = get_str_from_map(&map, "directory") { - self.log.directory = x; + if let Some(x) = get_bool_from_map(&map, "enable_console") { + self.log.enable_console = x; + } + if let Some(x) = get_bool_from_map(&map, "enable_file") { + self.log.enable_file = x; + } + if let Some(x) = get_str_from_map(&map, "file_directory") { + self.log.file_directory = x; } if let Some(x) = get_str_from_map(&map, "level") { self.log.level = x; @@ -416,7 +441,9 @@ retry: count: 2 interval: 5000 log: - directory: ./logs + enable_console: true + enable_file: true + file_directory: ./logs level: INFO data: response_identity: @@ -434,3 +461,8 @@ data: version: hash: "; + +#[test] +fn test_log() { + error!("error test"); +} diff --git a/src/socket.rs b/src/socket.rs index 842b9f9..484ce47 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1,4 +1,7 @@ use crate::settings::Settings; +#[cfg(feature = "nolog")] +use crate::{debug, error, info, log, trace, warn}; +#[cfg(not(feature = "nolog"))] use log::{error, info}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; use std::sync::Arc; @@ -100,3 +103,8 @@ impl Socket { Ok(v) } } + +#[test] +fn test() { + error!("error test"); +} diff --git a/src/udp.rs b/src/udp.rs index 20b30b3..dc22270 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -5,10 +5,13 @@ use std::time::Duration; use std::{ptr, thread}; use thread::{JoinHandle, Thread}; +#[cfg(feature = "nolog")] +use crate::{debug, error, info, log, trace, warn}; use bytes::BytesMut; use chrono::Local; use crossbeam::channel::TryRecvError; use crossbeam::{Receiver, Sender}; +#[cfg(not(feature = "nolog"))] use log::{debug, error, info}; use pnet::datalink::MacAddr; @@ -602,7 +605,11 @@ impl Process { #[test] fn test() { - let mut s = &mut String::new(); dbg!(encoding_rs::GB18030.decode_with_bom_removal(hex::decode("d7d432303139c4ea39d4c23239c8d5c6f0a3acc8e7d0e8d4dacee5c9bdd0a3c7f8b0ecc0edcdf8c2e7d6d0d0c4cfe0b9d8d2b5cef1a3acc7ebd2c6b2bdd6c1cee5c9bdd0a3c7f831bac5c2a5caa6c9fab7fecef1d6d0d0c4d2bbc2a5b6abb2e0b4f3ccfc31d6c135bac5b4b0bfdaa1a3cfeacfb8d0c5cfa2c7ebbcfb687474703a2f2f7765622e736375742e6564752e636e2f323031392f303932352f633135323835613333353931312f706167652e68746d00").unwrap().as_slice())); dbg!(encoding_rs::GB18030.decode_with_bom_removal(hex::decode("d6c2d0a3d4b0cdf8d3c3bba7a3accfd6d2d1b7a2b2bcc6bbb9fbb5e7c4d4d0c2b0e6c8cfd6a4bfcdbba7b6cba3acd6a7b3d6a1be6d61634f532031302e313520436174616c696e61a1bfa3acbfc9b5bd20687474703a2f2f3230322e33382e3139332e36352f20cfc2d4d8a1a3cad7b4ceb0b2d7b0c7b0a3acd0e8cfc8b6cfbfaad3d0cfdfc1acbdd3bbf2b0ceb3f6cdf8cfdfa3acc8bbbaf3b5c7c2bdcedecfdfcdf8c2e7a3acd4d9bdf8d0d0b0b2d7b0a1a3c8e7c4fad3d0c6e4cbfcd2c9cecaa3acbbb6d3add6").unwrap().as_slice())); } + +#[test] +fn test_log() { + debug!("debug test"); +} diff --git a/src/util.rs b/src/util.rs index 52e86bb..d658299 100644 --- a/src/util.rs +++ b/src/util.rs @@ -6,12 +6,54 @@ use std::net::IpAddr; use std::ops::Add; use std::time::Duration; -const MS: Duration = Duration::from_millis(1); +#[cfg(feature = "nolog")] +#[macro_export] +macro_rules! log { + ($($_:tt)+) => { + () + }; +} +#[cfg(feature = "nolog")] +#[macro_export] +macro_rules! trace { + ($($_:tt)+) => { + () + }; +} +#[cfg(feature = "nolog")] +#[macro_export] +macro_rules! debug { + ($($_:tt)+) => { + () + }; +} +#[cfg(feature = "nolog")] +#[macro_export] +macro_rules! info { + ($($_:tt)+) => { + () + }; +} +#[cfg(feature = "nolog")] +#[macro_export] +macro_rules! warn { + ($($_:tt)+) => { + () + }; +} +#[cfg(feature = "nolog")] +#[macro_export] +macro_rules! error { + ($($_:tt)+) => { + () + }; +} + +const MS: Duration = Duration::from_millis(10); #[inline] pub fn sleep() { std::thread::sleep(MS); - std::thread::yield_now(); } #[inline]