Add Mshroom machine implementation
[temp2RGB.git] / src / main.rs
index 014c61d..ed4eccb 100644 (file)
-#[macro_use]
-extern crate windows_service;
-
-use std::{
-    env,
-    ffi::OsString,
-    sync::{
-        atomic::{AtomicBool, Ordering},
-        Arc,
-    },
-    thread::sleep,
-    time::{self, Duration},
-};
-
-use anyhow::Result;
-use log::{debug, error, info, trace, warn};
-use windows::Win32::Foundation::{ERROR_SERVICE_DOES_NOT_EXIST, WIN32_ERROR};
-use windows_service::{
-    service::{
-        ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl, ServiceExitCode,
-        ServiceInfo, ServiceStartType, ServiceState, ServiceStatus, ServiceType,
-    },
-    service_control_handler::{self, ServiceControlHandlerResult, ServiceStatusHandle},
-    service_dispatcher,
-    service_manager::{ServiceManager, ServiceManagerAccess},
-};
-
-define_windows_service!(ffi_service_main, service_main);
-
-mod wrapper_winring0 {
-    #![allow(warnings, unused)]
-    include!(concat!(env!("OUT_DIR"), "/ols_api.rs"));
-}
-mod intel_arc {
-    #![allow(warnings, unused)]
-    include!(concat!(env!("OUT_DIR"), "/intel_arc.rs"));
-}
-mod a770;
-mod asus_aura_usb;
-mod corsair_lighting_pro;
-mod lian_li_sl_infinity;
-mod machine;
-mod main_loop;
-mod winring0;
-// mod common;
-mod consts;
-mod corsair_vengeance;
-mod piix4_i2c;
-mod rgb;
-// mod roccat; Disabled.
-mod cpu_temperature;
-mod settings;
-mod tests;
-mod timer;
-
-fn main() -> Result<()> {
-    let is_debug = cfg!(debug_assertions);
-
-    flexi_logger::Logger::try_with_str(if is_debug { "debug" } else { "info" })?
-        .log_to_file(
-            flexi_logger::FileSpec::default()
-                .directory(dirs::config_dir().unwrap().join(consts::SERVICE_NAME))
-                .basename(consts::SERVICE_NAME),
-        )
-        .duplicate_to_stdout(flexi_logger::Duplicate::All)
-        .format(if is_debug {
-            flexi_logger::default_format
-        } else {
-            flexi_logger::detailed_format
-        })
-        .rotate(
-            flexi_logger::Criterion::Size(1024 * 1024),
-            flexi_logger::Naming::Timestamps,
-            flexi_logger::Cleanup::KeepLogFiles(10),
-        )
-        .print_message()
-        .start()?;
-
-    log_panics::init();
-
-    let args: Vec<String> = env::args().collect();
-
-    info!("Temperature to RGB");
-
-    if args.contains(&"--no-service".to_string()) {
-        let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
-        main_loop::main_loop(completed.clone());
-    } else if args.contains(&"--tests".to_string()) {
-        tests::tests();
-    } else if args.contains(&"--install-service".to_string()) {
-        println!("Installing service...");
-        install_service()?;
-    } else if args.contains(&"--uninstall-service".to_string()) {
-        println!("Uninstalling service...");
-        uninstall_service()?;
-    } else {
-        service_dispatcher::start(consts::SERVICE_NAME, ffi_service_main)?;
-    }
-
-    Ok(())
-}
-
-fn install_service() -> windows_service::Result<()> {
-    let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
-    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
-
-    let service_binary_path = std::env::current_exe()
-        .unwrap()
-        .with_file_name("temp_2_rgb.exe");
-
-    println!("Installing service: {service_binary_path:?}");
-
-    let service_info = ServiceInfo {
-        name: OsString::from(consts::SERVICE_NAME),
-        display_name: OsString::from(consts::SERVICE_NAME),
-        service_type: ServiceType::OWN_PROCESS,
-        start_type: ServiceStartType::AutoStart,
-        error_control: ServiceErrorControl::Normal,
-        executable_path: service_binary_path,
-        launch_arguments: vec![],
-        dependencies: vec![],
-        account_name: None, // run as System
-        account_password: None,
-    };
-    let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
-    service.set_description(
-        "A service to set the color of hardware according to the temperature of GPU and CPU",
-    )?;
-    Ok(())
-}
-
-fn uninstall_service() -> windows_service::Result<()> {
-    let manager_access = ServiceManagerAccess::CONNECT;
-    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
-
-    let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
-    let service = service_manager.open_service(consts::SERVICE_NAME, service_access)?;
-
-    // The service will be marked for deletion as long as this function call succeeds.
-    // However, it will not be deleted from the database until it is stopped and all open handles to it are closed.
-    service.delete()?;
-
-    // Our handle to it is not closed yet. So we can still query it.
-    if service.query_status()?.current_state != ServiceState::Stopped {
-        // If the service cannot be stopped, it will be deleted when the system restarts.
-        service.stop()?;
-    }
-
-    // Explicitly close our open handle to the service. This is automatically called when `service` goes out of scope.
-    drop(service);
-
-    // Win32 API does not give us a way to wait for service deletion.
-    // To check if the service is deleted from the database, we have to poll it ourselves.
-    let start = time::Instant::now();
-    let timeout = Duration::from_secs(5);
-    while start.elapsed() < timeout {
-        if let Err(windows_service::Error::Winapi(e)) =
-            service_manager.open_service(consts::SERVICE_NAME, ServiceAccess::QUERY_STATUS)
-        {
-            let WIN32_ERROR(error_num) = ERROR_SERVICE_DOES_NOT_EXIST;
-            if e.raw_os_error() == Some(error_num as i32) {
-                println!("{} is deleted.", consts::SERVICE_NAME);
-                return Ok(());
-            }
-        }
-        sleep(Duration::from_secs(1));
-    }
-    println!("{} is marked for deletion.", consts::SERVICE_NAME);
-
-    Ok(())
-}
-
-fn service_main(arguments: Vec<OsString>) {
-    if let Err(error) = run_service(arguments) {
-        error!("{error}");
-    }
-}
-
-fn run_service(_arguments: Vec<OsString>) -> Result<(), windows_service::Error> {
-    let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
-
-    let completed_event_handler = Arc::clone(&completed);
-
-    info!("Setuping the event handler...");
-
-    let event_handler = move |control_event| -> ServiceControlHandlerResult {
-        match control_event {
-            ServiceControl::Stop => {
-                completed_event_handler.store(true, Ordering::Relaxed);
-                // Handle stop event and return control back to the system.
-                ServiceControlHandlerResult::NoError
-            }
-            ServiceControl::Shutdown => {
-                completed_event_handler.store(true, Ordering::Relaxed);
-                // Handle stop event and return control back to the system.
-                ServiceControlHandlerResult::NoError
-            }
-            // ServiceControl::Preshutdown => {
-            //     completed_event_handler.store(true, Ordering::Relaxed);
-            //     ServiceControlHandlerResult::NoError
-            // }
-            // ServiceControl::PowerEvent(param) => {
-            //     ServiceControlHandlerResult::NotImplemented
-            // }
-            // All services must accept Interrogate even if it's a no-op.
-            ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
-            _ => ServiceControlHandlerResult::NotImplemented,
-        }
-    };
-
-    // Register system service event handler
-    let status_handle = service_control_handler::register(consts::SERVICE_NAME, event_handler)?;
-
-    status_handle.set_service_status(ServiceStatus {
-        service_type: ServiceType::OWN_PROCESS,
-        current_state: ServiceState::Running,
-        controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN,
-        exit_code: ServiceExitCode::Win32(0),
-        checkpoint: 0,
-        wait_hint: Duration::default(),
-        process_id: None, //Some(std::process::id()),
-    })?;
-
-    main_loop::main_loop(completed.clone());
-
-    status_handle.set_service_status(ServiceStatus {
-        service_type: ServiceType::OWN_PROCESS,
-        current_state: ServiceState::Stopped,
-        controls_accepted: ServiceControlAccept::empty(),
-        exit_code: ServiceExitCode::Win32(0),
-        checkpoint: 0,
-        wait_hint: Duration::default(),
-        process_id: None, //Some(std::process::id()),
-    })?;
-
-    info!("Main loop stopped: Temperature to RGB will now shut down");
-
-    Ok(())
-}
+#[macro_use]\r
+extern crate windows_service;\r
+\r
+use std::{\r
+    ffi::OsString,\r
+    sync::{\r
+        Arc,\r
+        atomic::{AtomicBool, Ordering},\r
+    },\r
+    thread::sleep,\r
+    time::{self, Duration},\r
+};\r
+\r
+use anyhow::Result;\r
+use clap::Parser;\r
+use log::{error, info};\r
+use windows::Win32::Foundation::{ERROR_SERVICE_DOES_NOT_EXIST, WIN32_ERROR};\r
+use windows_service::{\r
+    service::{\r
+        ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl, ServiceExitCode,\r
+        ServiceInfo, ServiceStartType, ServiceState, ServiceStatus, ServiceType,\r
+    },\r
+    service_control_handler::{self, ServiceControlHandlerResult},\r
+    service_dispatcher,\r
+    service_manager::{ServiceManager, ServiceManagerAccess},\r
+};\r
+\r
+define_windows_service!(ffi_service_main, service_main);\r
+\r
+mod wrapper_winring0 {\r
+    #![allow(warnings, unused)]\r
+    include!(concat!(env!("OUT_DIR"), "/ols_api.rs"));\r
+}\r
+// mod intel_arc {\r
+//     #![allow(warnings, unused)]\r
+//     include!(concat!(env!("OUT_DIR"), "/intel_arc.rs"));\r
+// }\r
+mod a770;\r
+mod asus_aura_usb;\r
+mod consts;\r
+mod corsair_lighting_pro;\r
+mod corsair_vengeance;\r
+mod gigabyte_rgb_fusion_usb;\r
+mod lian_li_sl_infinity;\r
+mod machine;\r
+mod main_loop;\r
+mod piix4_i2c;\r
+mod rgb;\r
+mod winring0;\r
+// mod roccat; Disabled.\r
+mod cpu_temperature;\r
+mod settings;\r
+mod tests;\r
+mod timer;\r
+\r
+#[derive(Parser, Debug)]\r
+#[command(\r
+    author = "Greg Burri",\r
+    version = "1.0",\r
+    about = "Set RGB according to CPU and GPU temperaturess"\r
+)]\r
+struct Args {\r
+    /// Launch without service.\r
+    #[arg(group = "main", long)]\r
+    no_service: bool,\r
+\r
+    /// Run tests.\r
+    #[arg(group = "main", long)]\r
+    tests: bool,\r
+\r
+    /// Install driver winring0.\r
+    #[arg(group = "main", long)]\r
+    install_winring0: bool,\r
+\r
+    /// Install the service.\r
+    #[arg(group = "main", long)]\r
+    install_service: bool,\r
+\r
+    /// Uninstall the service.\r
+    #[arg(group = "main", long)]\r
+    uninstall_service: bool,\r
+}\r
+\r
+// Important: when starting as a service, the directory where the log and config files\r
+// are put is 'C:\Windows\System32\config\systemprofile\AppData\Roaming\Temp2RGB'.\r
+fn main() -> Result<()> {\r
+    let is_debug = cfg!(debug_assertions);\r
+\r
+    flexi_logger::Logger::try_with_str(if is_debug { "debug" } else { "info" })?\r
+        .log_to_file(\r
+            flexi_logger::FileSpec::default()\r
+                .directory(dirs::config_dir().unwrap().join(consts::SERVICE_NAME))\r
+                .basename(consts::SERVICE_NAME),\r
+        )\r
+        .duplicate_to_stdout(flexi_logger::Duplicate::All)\r
+        .format(if is_debug {\r
+            flexi_logger::default_format\r
+        } else {\r
+            flexi_logger::detailed_format\r
+        })\r
+        .rotate(\r
+            flexi_logger::Criterion::Size(1024 * 1024),\r
+            flexi_logger::Naming::Timestamps,\r
+            flexi_logger::Cleanup::KeepLogFiles(10),\r
+        )\r
+        .print_message()\r
+        .start()?;\r
+\r
+    log_panics::init();\r
+\r
+    let args = Args::parse();\r
+\r
+    info!("Temperature to RGB");\r
+\r
+    if args.no_service {\r
+        let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));\r
+        main_loop::main_loop(completed.clone());\r
+    } else if args.tests {\r
+        tests::tests();\r
+    } else if args.install_winring0 {\r
+        println!("Installing winring0 service...");\r
+        install_winring0()?;\r
+    } else if args.install_service {\r
+        println!("Installing service...");\r
+        install_service()?;\r
+    } else if args.uninstall_service {\r
+        println!("Uninstalling service...");\r
+        uninstall_service()?;\r
+    } else {\r
+        service_dispatcher::start(consts::SERVICE_NAME, ffi_service_main)?;\r
+    }\r
+\r
+    Ok(())\r
+}\r
+\r
+fn install_winring0() -> windows_service::Result<()> {\r
+    let system_dir = unsafe {\r
+        let mut system_dir = [0u8; windows::Win32::Foundation::MAX_PATH as usize];\r
+        let l =\r
+            windows::Win32::System::SystemInformation::GetSystemDirectoryA(Some(&mut system_dir))\r
+                as usize;\r
+        assert_ne!(l, 0);\r
+        String::from_utf8(system_dir[0..l].into()).unwrap()\r
+    };\r
+    // TODO: to const.\r
+    let winring0_filename = "WinRing0x64.sys";\r
+    let driver_name = "WinRing0x64";\r
+\r
+    let winring0_path = std::env::current_exe()\r
+        .unwrap()\r
+        .with_file_name(winring0_filename);\r
+\r
+    let destination = std::path::Path::new(&system_dir)\r
+        .join("drivers")\r
+        .join(winring0_filename);\r
+\r
+    std::fs::copy(winring0_path, &destination).unwrap();\r
+\r
+    let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;\r
+    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;\r
+\r
+    // println!("Installing service: {service_binary_path:?}");\r
+\r
+    let service_info = ServiceInfo {\r
+        name: OsString::from(driver_name),\r
+        display_name: OsString::from(driver_name),\r
+        service_type: ServiceType::KERNEL_DRIVER,\r
+        start_type: ServiceStartType::AutoStart,\r
+        error_control: ServiceErrorControl::Normal,\r
+        executable_path: destination,\r
+        launch_arguments: vec![],\r
+        dependencies: vec![],\r
+        account_name: None, // run as System\r
+        account_password: None,\r
+    };\r
+\r
+    let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;\r
+    service.set_description("Winring0")?;\r
+\r
+    Ok(())\r
+}\r
+\r
+fn install_service() -> windows_service::Result<()> {\r
+    let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;\r
+    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;\r
+\r
+    let service_binary_path = std::env::current_exe()\r
+        .unwrap()\r
+        .with_file_name("temp_2_rgb.exe");\r
+\r
+    println!("Installing service: {service_binary_path:?}");\r
+\r
+    let service_info = ServiceInfo {\r
+        name: OsString::from(consts::SERVICE_NAME),\r
+        display_name: OsString::from(consts::SERVICE_NAME),\r
+        service_type: ServiceType::OWN_PROCESS,\r
+        start_type: ServiceStartType::AutoStart,\r
+        error_control: ServiceErrorControl::Normal,\r
+        executable_path: service_binary_path,\r
+        launch_arguments: vec![],\r
+        dependencies: vec![],\r
+        account_name: None, // run as System\r
+        account_password: None,\r
+    };\r
+\r
+    let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;\r
+    service.set_description(\r
+        "A service to set the color of hardware according to the temperature of GPU and CPU",\r
+    )?;\r
+\r
+    Ok(())\r
+}\r
+\r
+fn uninstall_service() -> windows_service::Result<()> {\r
+    let manager_access = ServiceManagerAccess::CONNECT;\r
+    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;\r
+\r
+    let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;\r
+    let service = service_manager.open_service(consts::SERVICE_NAME, service_access)?;\r
+\r
+    // The service will be marked for deletion as long as this function call succeeds.\r
+    // However, it will not be deleted from the database until it is stopped and all open handles to it are closed.\r
+    service.delete()?;\r
+\r
+    // Our handle to it is not closed yet. So we can still query it.\r
+    if service.query_status()?.current_state != ServiceState::Stopped {\r
+        // If the service cannot be stopped, it will be deleted when the system restarts.\r
+        service.stop()?;\r
+    }\r
+\r
+    // Explicitly close our open handle to the service. This is automatically called when `service` goes out of scope.\r
+    drop(service);\r
+\r
+    // Win32 API does not give us a way to wait for service deletion.\r
+    // To check if the service is deleted from the database, we have to poll it ourselves.\r
+    let start = time::Instant::now();\r
+    let timeout = Duration::from_secs(5);\r
+    while start.elapsed() < timeout {\r
+        if let Err(windows_service::Error::Winapi(e)) =\r
+            service_manager.open_service(consts::SERVICE_NAME, ServiceAccess::QUERY_STATUS)\r
+        {\r
+            let WIN32_ERROR(error_num) = ERROR_SERVICE_DOES_NOT_EXIST;\r
+            if e.raw_os_error() == Some(error_num as i32) {\r
+                println!("{} is deleted.", consts::SERVICE_NAME);\r
+                return Ok(());\r
+            }\r
+        }\r
+        sleep(Duration::from_secs(1));\r
+    }\r
+    println!("{} is marked for deletion.", consts::SERVICE_NAME);\r
+\r
+    Ok(())\r
+}\r
+\r
+fn service_main(arguments: Vec<OsString>) {\r
+    if let Err(error) = run_service(arguments) {\r
+        error!("{error}");\r
+    }\r
+}\r
+\r
+fn run_service(_arguments: Vec<OsString>) -> Result<(), windows_service::Error> {\r
+    let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));\r
+\r
+    let completed_event_handler = Arc::clone(&completed);\r
+\r
+    info!("Setup the event handler...");\r
+\r
+    let event_handler = move |control_event| -> ServiceControlHandlerResult {\r
+        match control_event {\r
+            ServiceControl::Stop => {\r
+                completed_event_handler.store(true, Ordering::Relaxed);\r
+                // Handle stop event and return control back to the system.\r
+                ServiceControlHandlerResult::NoError\r
+            }\r
+            ServiceControl::Shutdown => {\r
+                completed_event_handler.store(true, Ordering::Relaxed);\r
+                // Handle stop event and return control back to the system.\r
+                ServiceControlHandlerResult::NoError\r
+            }\r
+            // ServiceControl::Preshutdown => {\r
+            //     completed_event_handler.store(true, Ordering::Relaxed);\r
+            //     ServiceControlHandlerResult::NoError\r
+            // }\r
+            // ServiceControl::PowerEvent(param) => {\r
+            //     ServiceControlHandlerResult::NotImplemented\r
+            // }\r
+            // All services must accept Interrogate even if it's a no-op.\r
+            ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,\r
+            _ => ServiceControlHandlerResult::NotImplemented,\r
+        }\r
+    };\r
+\r
+    // Register system service event handler\r
+    let status_handle = service_control_handler::register(consts::SERVICE_NAME, event_handler)?;\r
+\r
+    status_handle.set_service_status(ServiceStatus {\r
+        service_type: ServiceType::OWN_PROCESS,\r
+        current_state: ServiceState::Running,\r
+        controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN,\r
+        exit_code: ServiceExitCode::Win32(0),\r
+        checkpoint: 0,\r
+        wait_hint: Duration::default(),\r
+        process_id: None, //Some(std::process::id()),\r
+    })?;\r
+\r
+    main_loop::main_loop(completed.clone());\r
+\r
+    status_handle.set_service_status(ServiceStatus {\r
+        service_type: ServiceType::OWN_PROCESS,\r
+        current_state: ServiceState::Stopped,\r
+        controls_accepted: ServiceControlAccept::empty(),\r
+        exit_code: ServiceExitCode::Win32(0),\r
+        checkpoint: 0,\r
+        wait_hint: Duration::default(),\r
+        process_id: None, //Some(std::process::id()),\r
+    })?;\r
+\r
+    info!("Main loop stopped: Temperature to RGB will now shut down");\r
+\r
+    Ok(())\r
+}\r