Is there proccess monitoring in std?

I look windows processes and manually kill some processes (feedback, telemetry, etc..)

So, now I want to create my 1-st little real system program using system program language Rust.

? 1a. Has std::* library some functions for monitoring OS processes? Or I have to use a crate from Crares.io ?

? 1b. How to start/stop windows services ? ( net stop/start ... , etc..)

? 2. How to keep running rust program: instead of start->main()->exit start->main() ... ->exitbyuser

You can use net start/stop as you said. There are probably also crates for it.

Don't return from main and your program will keep running.

2 Likes

No, there isn't. You can only work with processes you spawn yourself via Command.

std isn't meant to be comprehensive. It's kept minimal on purpose. Expect almost everything in Rust to be done via 3rd party crates or interact directly with OS APIs.

5 Likes

Here it is :wink:

For example, starting and stopping services with OS APIs would look like the C++ example at Starting a Service - Win32 apps | Microsoft Docs, but adapted for Rust. With some wrappers around handles for convenience, that can look like this:

use std::marker::PhantomData;
use std::mem::{size_of, MaybeUninit};
use std::thread::sleep;
use std::time::{Instant, Duration};

use windows::core::HSTRING;
use windows::w;
use windows::Win32::Security::SC_HANDLE;
use windows::Win32::System::Services::{CloseServiceHandle, OpenSCManagerW, SC_MANAGER_ALL_ACCESS, OpenServiceW, SERVICE_ALL_ACCESS, QueryServiceStatusEx, SC_STATUS_PROCESS_INFO, SERVICE_STATUS_PROCESS, SERVICE_STOPPED, SERVICE_STOP_PENDING, StartServiceW, SERVICE_START_PENDING, SERVICE_RUNNING};

fn main() {
    do_start_service().unwrap();
}

struct SCManagerHandle(SC_HANDLE);

impl SCManagerHandle {
    pub fn open() -> windows::core::Result<Self> {
        let handle = unsafe {
            OpenSCManagerW(None, None, SC_MANAGER_ALL_ACCESS)?
        };
        Ok(SCManagerHandle(handle))
    }

    pub fn open_service(&self, servicename: &HSTRING) -> windows::core::Result<ServiceHandle> {
        let handle = unsafe {
            OpenServiceW(self.0, servicename, SERVICE_ALL_ACCESS)?
        };
        Ok(ServiceHandle(handle, PhantomData))
    }
}

impl Drop for SCManagerHandle {
    fn drop(&mut self) {
        unsafe { CloseServiceHandle(self.0); }
    }
}

struct ServiceHandle<'a>(SC_HANDLE, PhantomData<&'a SCManagerHandle>);

impl ServiceHandle<'_> {
    pub fn query_status(&self) -> windows::core::Result<SERVICE_STATUS_PROCESS> {
        let mut bytes_needed = 0u32;
        let mut status: MaybeUninit<SERVICE_STATUS_PROCESS> = MaybeUninit::uninit();
        unsafe {
            QueryServiceStatusEx(
                self.0, 
                SC_STATUS_PROCESS_INFO, 
                status.as_mut_ptr().cast(), 
                size_of::<SERVICE_STATUS_PROCESS>() as u32, 
                &mut bytes_needed
            ).ok().map(|_| status.assume_init())
        }
    }

    pub fn start(&self) -> windows::core::Result<()> {
        unsafe {
            StartServiceW(self.0, &[]).ok()
        }
    }
}

impl Drop for ServiceHandle<'_> {
    fn drop(&mut self) {
        unsafe { CloseServiceHandle(self.0); }
    }
}

fn do_start_service() -> windows::core::Result<()> {
    let sc_manager = SCManagerHandle::open()?;
    let service = sc_manager.open_service(w!("ssh-agent"))?;

    let mut status = service.query_status()?;
    if status.dwCurrentState != SERVICE_STOPPED && status.dwCurrentState != SERVICE_STOP_PENDING {
        eprintln!("Service is already running");
        return Ok(());
    }

    let mut start_time = Instant::now();
    let mut old_checkpoint = status.dwCheckPoint;
    while status.dwCurrentState == SERVICE_STOP_PENDING {
        let wait_time = (status.dwWaitHint / 10).clamp(1000, 10000);
        sleep(Duration::from_millis(wait_time.into()));
        status = service.query_status()?;
        if status.dwCheckPoint > old_checkpoint {
            start_time = Instant::now();
            old_checkpoint = status.dwCheckPoint;
        } else if Instant::now().duration_since(start_time) > Duration::from_millis(status.dwWaitHint.into()) {
            eprintln!("Timeout waiting for service to stop.");
            return Ok(());
        }
    }

    service.start()?;
    println!("Service start pending...");

    status = service.query_status()?;
    start_time = Instant::now();
    old_checkpoint = status.dwCheckPoint;
    while status.dwCurrentState == SERVICE_START_PENDING {
        let wait_time = (status.dwWaitHint / 10).clamp(1000, 10000);
        sleep(Duration::from_millis(wait_time.into()));
        status = service.query_status()?;
        if status.dwCheckPoint > old_checkpoint {
            start_time = Instant::now();
            old_checkpoint = status.dwCheckPoint;
        } else if Instant::now().duration_since(start_time) > Duration::from_millis(status.dwWaitHint.into()) {
            break;
        }
    }

    if status.dwCurrentState == SERVICE_RUNNING {
        println!("Service started successfully.");
    } else {
        eprintln!("Service not started.");
        eprintln!("  Current state: {:?}", status.dwCurrentState);
        eprintln!("  Exit code: {:08X}", status.dwWin32ExitCode);
        eprintln!("  Check point: {}", status.dwCheckPoint);
        eprintln!("  Wait hint: {}", status.dwWaitHint);
    }
    Ok(())
}
2 Likes