Add a color (2 colors can be now defined for a machine).
[temp2RGB.git] / src / main.rs
1 #[macro_use]
2 extern crate windows_service;
3
4 use std::{
5 env,
6 ffi::OsString,
7 sync::{
8 atomic::{AtomicBool, Ordering},
9 Arc,
10 },
11 thread::sleep,
12 time::{self, Duration},
13 };
14
15 use anyhow::Result;
16 use log::{debug, error, info, trace, warn};
17 use windows::Win32::Foundation::{ERROR_SERVICE_DOES_NOT_EXIST, WIN32_ERROR};
18 use windows_service::{
19 service::{
20 ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl, ServiceExitCode,
21 ServiceInfo, ServiceStartType, ServiceState, ServiceStatus, ServiceType,
22 },
23 service_control_handler::{self, ServiceControlHandlerResult, ServiceStatusHandle},
24 service_dispatcher,
25 service_manager::{ServiceManager, ServiceManagerAccess},
26 };
27
28 define_windows_service!(ffi_service_main, service_main);
29
30 mod wrapper_winring0 {
31 #![allow(warnings, unused)]
32 include!(concat!(env!("OUT_DIR"), "/ols_api.rs"));
33 }
34 mod intel_arc {
35 #![allow(warnings, unused)]
36 include!(concat!(env!("OUT_DIR"), "/intel_arc.rs"));
37 }
38 mod a770;
39 mod asus_aura_usb;
40 mod corsair_lighting_pro;
41 mod lian_li_sl_infinity;
42 mod machine;
43 mod main_loop;
44 mod winring0;
45 // mod common;
46 mod consts;
47 mod corsair_vengeance;
48 mod piix4_i2c;
49 mod rgb;
50 // mod roccat; Disabled.
51 mod cpu_temperature;
52 mod settings;
53 mod tests;
54 mod timer;
55
56 // Important: when starting as a service, the directory where the log and config files
57 // are put is 'C:\Windows\System32\config\systemprofile\AppData\Roaming\Temp2RGB'.
58 fn main() -> Result<()> {
59 let is_debug = cfg!(debug_assertions);
60
61 flexi_logger::Logger::try_with_str(if is_debug { "debug" } else { "info" })?
62 .log_to_file(
63 flexi_logger::FileSpec::default()
64 .directory(dirs::config_dir().unwrap().join(consts::SERVICE_NAME))
65 .basename(consts::SERVICE_NAME),
66 )
67 .duplicate_to_stdout(flexi_logger::Duplicate::All)
68 .format(if is_debug {
69 flexi_logger::default_format
70 } else {
71 flexi_logger::detailed_format
72 })
73 .rotate(
74 flexi_logger::Criterion::Size(1024 * 1024),
75 flexi_logger::Naming::Timestamps,
76 flexi_logger::Cleanup::KeepLogFiles(10),
77 )
78 .print_message()
79 .start()?;
80
81 log_panics::init();
82
83 let args: Vec<String> = env::args().collect();
84
85 info!("Temperature to RGB");
86
87 if args.contains(&"--no-service".to_string()) {
88 let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
89 main_loop::main_loop(completed.clone());
90 } else if args.contains(&"--tests".to_string()) {
91 tests::tests();
92 } else if args.contains(&"--install-service".to_string()) {
93 println!("Installing service...");
94 install_service()?;
95 } else if args.contains(&"--uninstall-service".to_string()) {
96 println!("Uninstalling service...");
97 uninstall_service()?;
98 } else {
99 service_dispatcher::start(consts::SERVICE_NAME, ffi_service_main)?;
100 }
101
102 Ok(())
103 }
104
105 fn install_service() -> windows_service::Result<()> {
106 let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
107 let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
108
109 let service_binary_path = std::env::current_exe()
110 .unwrap()
111 .with_file_name("temp_2_rgb.exe");
112
113 println!("Installing service: {service_binary_path:?}");
114
115 let service_info = ServiceInfo {
116 name: OsString::from(consts::SERVICE_NAME),
117 display_name: OsString::from(consts::SERVICE_NAME),
118 service_type: ServiceType::OWN_PROCESS,
119 start_type: ServiceStartType::AutoStart,
120 error_control: ServiceErrorControl::Normal,
121 executable_path: service_binary_path,
122 launch_arguments: vec![],
123 dependencies: vec![],
124 account_name: None, // run as System
125 account_password: None,
126 };
127 let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
128 service.set_description(
129 "A service to set the color of hardware according to the temperature of GPU and CPU",
130 )?;
131 Ok(())
132 }
133
134 fn uninstall_service() -> windows_service::Result<()> {
135 let manager_access = ServiceManagerAccess::CONNECT;
136 let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
137
138 let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
139 let service = service_manager.open_service(consts::SERVICE_NAME, service_access)?;
140
141 // The service will be marked for deletion as long as this function call succeeds.
142 // However, it will not be deleted from the database until it is stopped and all open handles to it are closed.
143 service.delete()?;
144
145 // Our handle to it is not closed yet. So we can still query it.
146 if service.query_status()?.current_state != ServiceState::Stopped {
147 // If the service cannot be stopped, it will be deleted when the system restarts.
148 service.stop()?;
149 }
150
151 // Explicitly close our open handle to the service. This is automatically called when `service` goes out of scope.
152 drop(service);
153
154 // Win32 API does not give us a way to wait for service deletion.
155 // To check if the service is deleted from the database, we have to poll it ourselves.
156 let start = time::Instant::now();
157 let timeout = Duration::from_secs(5);
158 while start.elapsed() < timeout {
159 if let Err(windows_service::Error::Winapi(e)) =
160 service_manager.open_service(consts::SERVICE_NAME, ServiceAccess::QUERY_STATUS)
161 {
162 let WIN32_ERROR(error_num) = ERROR_SERVICE_DOES_NOT_EXIST;
163 if e.raw_os_error() == Some(error_num as i32) {
164 println!("{} is deleted.", consts::SERVICE_NAME);
165 return Ok(());
166 }
167 }
168 sleep(Duration::from_secs(1));
169 }
170 println!("{} is marked for deletion.", consts::SERVICE_NAME);
171
172 Ok(())
173 }
174
175 fn service_main(arguments: Vec<OsString>) {
176 if let Err(error) = run_service(arguments) {
177 error!("{error}");
178 }
179 }
180
181 fn run_service(_arguments: Vec<OsString>) -> Result<(), windows_service::Error> {
182 let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
183
184 let completed_event_handler = Arc::clone(&completed);
185
186 info!("Setuping the event handler...");
187
188 let event_handler = move |control_event| -> ServiceControlHandlerResult {
189 match control_event {
190 ServiceControl::Stop => {
191 completed_event_handler.store(true, Ordering::Relaxed);
192 // Handle stop event and return control back to the system.
193 ServiceControlHandlerResult::NoError
194 }
195 ServiceControl::Shutdown => {
196 completed_event_handler.store(true, Ordering::Relaxed);
197 // Handle stop event and return control back to the system.
198 ServiceControlHandlerResult::NoError
199 }
200 // ServiceControl::Preshutdown => {
201 // completed_event_handler.store(true, Ordering::Relaxed);
202 // ServiceControlHandlerResult::NoError
203 // }
204 // ServiceControl::PowerEvent(param) => {
205 // ServiceControlHandlerResult::NotImplemented
206 // }
207 // All services must accept Interrogate even if it's a no-op.
208 ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
209 _ => ServiceControlHandlerResult::NotImplemented,
210 }
211 };
212
213 // Register system service event handler
214 let status_handle = service_control_handler::register(consts::SERVICE_NAME, event_handler)?;
215
216 status_handle.set_service_status(ServiceStatus {
217 service_type: ServiceType::OWN_PROCESS,
218 current_state: ServiceState::Running,
219 controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN,
220 exit_code: ServiceExitCode::Win32(0),
221 checkpoint: 0,
222 wait_hint: Duration::default(),
223 process_id: None, //Some(std::process::id()),
224 })?;
225
226 main_loop::main_loop(completed.clone());
227
228 status_handle.set_service_status(ServiceStatus {
229 service_type: ServiceType::OWN_PROCESS,
230 current_state: ServiceState::Stopped,
231 controls_accepted: ServiceControlAccept::empty(),
232 exit_code: ServiceExitCode::Win32(0),
233 checkpoint: 0,
234 wait_hint: Duration::default(),
235 process_id: None, //Some(std::process::id()),
236 })?;
237
238 info!("Main loop stopped: Temperature to RGB will now shut down");
239
240 Ok(())
241 }