Skip to content

Commit

Permalink
New year, new cleanup - dependencies, edition 2021, etc
Browse files Browse the repository at this point in the history
  • Loading branch information
OtaK committed Jan 3, 2022
1 parent c14943e commit 087ba2b
Show file tree
Hide file tree
Showing 21 changed files with 682 additions and 398 deletions.
265 changes: 145 additions & 120 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 7 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "timerset"
version = "0.4.0"
version = "0.4.1"
authors = ["Mathieu Amiot <[email protected]>"]
edition = "2018"
edition = "2021"

[package.metadata.winres]
OriginalFileName = "TimerSet.exe"
Expand All @@ -14,15 +14,12 @@ default = []

[dependencies]
log = "0.4"
paw = "1.0"
winreg = "0.8"
fern = { version = "0.6", features = ["colored"] }
chrono = "0.4"

[dependencies.structopt]
version = "0.3"
default-features = false
features = ["paw"]
clap = { version = "3.0", features = ["derive"] }
thiserror = "1.0"
eyre = "0.6"
time = { version = "0.3", features = ["formatting"] }
winreg = "0.10"

[dependencies.ntapi]
version = "0.3"
Expand Down
81 changes: 55 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,68 @@ The program should be run from an elevated (ie. cmd => run as administrator) ter
Obviously, it only works on Windows. Confirmed to work on Windows 10 2004, but should run on pretty much any version of Windows since the used APIs are so old.

```text
timerset 0.3.0
timerset 0.4.1
Mathieu Amiot <[email protected]>
TimerSet allows you to change your NT Kernel system timer
Also allows you to monitor Windows Standby List and clean it up when needed
TimerSet allows you to change your NT Kernel system timer Also allows you to monitor Windows Standby
List and clean it up when needed
USAGE:
timerset.exe [FLAGS] [OPTIONS]
FLAGS:
--islc Enables Windows Standby List periodic cleaning. It is akin to how ISLC by Wagnard works. Please
note that when enabling this, the program will **NOT** be idle at all times and will periodically
poll the system memory to check whether a cleanup is needed or not
-h, --help Prints help information
-i, --install Installs TimerSet to your system and runs it on startup
-u, --uninstall Uninstalls TimerSet from your system
-v, --values Prints the possible timer value range for your system. Please note that it can depend on many
factors such as HPET or dynamic/synthetic timers enabled or disabled
-V, --version Prints version information
timerset.exe [OPTIONS]
OPTIONS:
--islc-timer <clean-standby-list-poll-freq>
Standby List periodic cleaning poll interval. Defaults to 10 seconds which should be enough for most systems
without impacting performance [default: 10]
--cscm <clear-standby-cached-mem>
Cached memory threshold where the Windows Standby List will be cleared (in MB) Defaults to 1024MB (1GB)
--cscm <CLEAR_STANDBY_CACHED_MEM>
Cached memory threshold where the Windows Standby List will be cleared (in MB) Defaults
to 1024MB (1GB)
[default: 1024]
--csfm <clear-standby-free-mem>
Free memory threshold where the Windows Standby List will be cleared (in MB) Defaults to 1024MB (1GB)
--csfm <CLEAR_STANDBY_FREE_MEM>
Free memory threshold where the Windows Standby List will be cleared (in MB) Defaults to
1024MB (1GB)
[default: 1024]
-t, --timer <timer>
Allows to set a custom timer value in μs. Will be clamped between the bounds of allowed timer values. Also
note that sometimes, setting high timer values are rejected by the system and will be lowered down depending
on which clock source your system is using (TSC tends to lower values by 5μs, HPET does not for instance)
-h, --help
Print help information
-i, --install
Installs TimerSet to your system and runs it on startup
--islc
Enables Windows Standby List periodic cleaning. It is akin to how ISLC by Wagnard works
--islc-timer <CLEAN_STANDBY_LIST_POLL_FREQ>
Standby List anti-kernel DOS throttle timer It exists because
CreateMemoryResourceNotification can trigger LowMemoryResourceNotifications thousands of
times per second when they happen (i.e. every system page allocation in a high memory
pressure situation, often 4KB) resulting in the memory list cleaning paralyzing the
system with thousands of tries per second
Defaults to 10 seconds which should be enough for most systems without impacting
performance.
[default: 10]
-p, --pretend
Shows the actions taken but do not modify anything on the system; Also known as a dry
run
-t, --timer <TIMER>
Allows to set a custom timer value in μs. Will be clamped between the bounds of allowed
timer values. Also note that sometimes, setting high timer values are rejected by the
system and will be lowered down depending on which clock source your system is using
(TSC tends to lower values by ~5μs, HPET does not for instance)
-u, --uninstall
Uninstalls TimerSet from your system
-v, --values
Prints the possible timer value range for your system. Please note that it can depend on
many factors such as HPET or dynamic/synthetic timers enabled or disabled
-V, --version
Print version information
```

## Examples
Expand Down
23 changes: 23 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#[derive(Debug, thiserror::Error)]
pub enum TimersetError {
#[error(transparent)]
TaskSchedulerError(#[from] crate::task_scheduler::TaskSchedulerError),
#[error(transparent)]
EnvVarError(#[from] std::env::VarError),
#[error(transparent)]
SetLoggerError(#[from] log::SetLoggerError),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error("WindowsError: {0}")]
WindowsError(std::io::Error),
#[error(transparent)]
Other(#[from] eyre::Report),
}

impl TimersetError {
pub fn windows_error() -> Self {
Self::WindowsError(std::io::Error::last_os_error())
}
}

pub type TimersetResult<T> = Result<T, TimersetError>;
41 changes: 19 additions & 22 deletions src/install.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use crate::task_scheduler::{ExecAction, WindowsTaskScheduler};
use crate::task_scheduler::{ExecAction, RegisterTaskDefinitionArgs, WindowsTaskScheduler};
use crate::TimersetResult;
use crate::{
task_scheduler::{
LogonTrigger,
TaskActionType,
TaskCompatibility,
TaskInstancesPolicy,
TaskTriggerType,
TaskLogonType,
TaskRunlevel
LogonTrigger, TaskActionType, TaskCompatibility, TaskInstancesPolicy, TaskLogonType,
TaskRunlevel, TaskTriggerType,
},
utils::StartArgs,
};
Expand All @@ -19,12 +15,12 @@ const TASK_NAME: &str = "Start TimerSet [DEV]";
#[cfg(not(debug))]
const TASK_NAME: &str = "Start TimerSet";

pub fn install(args: &crate::Opts) -> std::io::Result<()> {
pub fn install(args: &crate::Opts) -> crate::TimersetResult<()> {
// Copy exe to %ProgramFiles%\TimerSet\TimerSet.exe
let current_exe_path = std::env::current_exe()?;
debug!("Current exe path: {:?}", current_exe_path);

let mut dest_path: std::path::PathBuf = std::env::var("PROGRAMFILES").unwrap().into();
let mut dest_path: std::path::PathBuf = std::env::var("PROGRAMFILES")?.into();
dest_path.push("TimerSet");
info!("Installing TimerSet at: {:?}", dest_path);
debug!("Creating app folder");
Expand Down Expand Up @@ -60,7 +56,8 @@ pub fn install(args: &crate::Opts) -> std::io::Result<()> {

let task_reginfo = task.registration_info()?;
task_reginfo.set_author("Mathieu \"OtaK_\" Amiot")?;
task_reginfo.set_description("Start TimerSet at logon of any user with admin permissions")?;
task_reginfo
.set_description("Start TimerSet at logon of any user with admin permissions")?;

let task_settings = task.settings()?;
task_settings.set_start_when_available(true)?;
Expand All @@ -87,23 +84,23 @@ pub fn install(args: &crate::Opts) -> std::io::Result<()> {
exec_action.set_working_directory(start_location)?;
}

let _ = folder.register_task_definition(
TASK_NAME,
task,
TASK_CREATE_OR_UPDATE as _,
None,
None,
TaskLogonType::InteractiveToken,
None,
)?;
let _ = folder.register_task_definition(RegisterTaskDefinitionArgs {
task_name: TASK_NAME,
task_definition: task,
flags: TASK_CREATE_OR_UPDATE as _,
user_id: None,
password: None,
logon_type: TaskLogonType::InteractiveToken,
sddl: None,
})?;
}

info!("Installation complete");

Ok(())
}

pub fn uninstall(args: &crate::Opts) -> std::io::Result<()> {
pub fn uninstall(args: &crate::Opts) -> TimersetResult<()> {
let scheduler = WindowsTaskScheduler::new()?;
scheduler.connect()?;

Expand All @@ -113,7 +110,7 @@ pub fn uninstall(args: &crate::Opts) -> std::io::Result<()> {
}

// Delete files
let mut dest_path: std::path::PathBuf = std::env::var("PROGRAMFILES").unwrap().into();
let mut dest_path: std::path::PathBuf = std::env::var("PROGRAMFILES")?.into();
dest_path.push("TimerSet");
debug!("Installation path to be removed: {:?}", dest_path);
if !args.pretend {
Expand Down
60 changes: 36 additions & 24 deletions src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,26 @@ pub struct Logger {

impl Logger {
pub fn new() -> Self {
Self {
has_init: false,
}
Self { has_init: false }
}

pub fn init(&mut self) {
pub fn init(&mut self) -> crate::TimersetResult<()> {
if self.has_init {
return;
return Ok(());
}

fern::Dispatch::new()
.chain(stdout())
.chain(filelog())
.apply()
.unwrap();
.chain(stdout()?)
.chain(filelog()?)
.apply()?;

self.has_init = true;

Ok(())
}
}

fn stdout() -> fern::Dispatch {
fn stdout() -> crate::TimersetResult<fern::Dispatch> {
let colors = ColoredLevelConfig::new()
.error(Color::Red)
.warn(Color::Yellow)
Expand All @@ -36,33 +35,46 @@ fn stdout() -> fern::Dispatch {
.trace(Color::BrightBlack)
.info(Color::Green);

fern::Dispatch::new()
let time_format = time::format_description::well_known::Rfc3339;

let dispatcher = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"[{target}] {level} > {message}",
level = colors.color(record.level()),
"[{time}][{target}] {level} > {message}",
time = time::OffsetDateTime::now_utc()
.format(&time_format)
.unwrap(),
target = record.target(),
level = colors.color(record.level()),
message = message,
))
})
.level(log::LevelFilter::Info)
.chain(std::io::stdout())
.chain(std::io::stdout());

Ok(dispatcher)
}

fn filelog() -> fern::Dispatch {
let mut logpath = std::env::current_exe().unwrap();
fn filelog() -> crate::TimersetResult<fern::Dispatch> {
let mut logpath = std::env::current_exe()?;
let _ = logpath.set_extension("log");

fern::Dispatch::new()
.format(|out, message, record| {
let time_format = time::format_description::well_known::Rfc3339;

let dispatcher = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"[{}][{}][{}] > {}",
chrono::Local::now().format("%Y-%m-%dT%H:%M:%S"),
record.level(),
record.target(),
message,
"[{time}][{level}][{target}] > {message}",
time = time::OffsetDateTime::now_utc()
.format(&time_format)
.unwrap(),
level = record.level(),
target = record.target(),
message = message,
))
})
.level(log::LevelFilter::Debug)
.chain(fern::log_file(logpath).unwrap())
.chain(fern::log_file(logpath)?);

Ok(dispatcher)
}
Loading

0 comments on commit 087ba2b

Please sign in to comment.