tcrm_task/tasks/process/group/action/
stop.rs

1use crate::tasks::process::group::builder::ProcessGroup;
2use crate::tasks::process::group::error::ProcessGroupError;
3
4impl ProcessGroup {
5    /// Terminates all processes in the group (Unix).
6    ///
7    /// Sends SIGTERM to the process group using killpg().
8    ///
9    /// # Returns
10    ///
11    /// * `Ok(())` - If the signal was sent successfully or processes were already terminated
12    /// * `Err(ProcessGroupError)` - If termination fails due to permissions or other errors
13    ///
14    /// # Example
15    /// ```rust,no_run
16    /// use tcrm_task::tasks::process::group::builder::ProcessGroup;
17    /// let mut group = ProcessGroup::new();
18    /// // ... spawn processes in the group ...
19    /// group.stop_group().unwrap();
20    /// ```
21    #[cfg(unix)]
22    pub fn stop_group(&self) -> Result<(), ProcessGroupError> {
23        use nix::sys::signal::{Signal, killpg};
24        use nix::unistd::Pid;
25        if let Some(pgid) = self.inner.process_group_id {
26            use nix::errno::Errno;
27            match killpg(Pid::from_raw(pgid), Signal::SIGTERM) {
28                Ok(_) => Ok(()),
29                Err(e) => match e {
30                    Errno::ESRCH => Ok(()), // Already terminated
31                    Errno::EPERM => Err(ProcessGroupError::SignalFailed(format!(
32                        "Permission denied to terminate process group {}",
33                        pgid
34                    ))),
35                    _ => Err(ProcessGroupError::SignalFailed(format!(
36                        "Failed to send SIGTERM to process group {}: {}",
37                        pgid, e
38                    ))),
39                },
40            }
41        } else {
42            Err(ProcessGroupError::SignalFailed(
43                "No process group ID available".to_string(),
44            ))
45        }
46    }
47
48    /// Terminates all processes in the job object (Windows).
49    ///
50    /// Terminates all processes in the job object using TerminateJobObject.
51    ///
52    /// # Returns
53    ///
54    /// * `Ok(())` - If the job was terminated successfully or was already terminated
55    /// * `Err(ProcessGroupError)` - If termination fails due to permissions or other errors
56    ///
57    /// # Example
58    /// ```rust,no_run
59    /// use tcrm_task::tasks::process::group::builder::ProcessGroup;
60    /// let mut group = ProcessGroup::new();
61    /// // ... spawn processes in the group ...
62    /// group.stop_group().unwrap();
63    /// ```
64    #[cfg(windows)]
65    pub fn stop_group(&self) -> Result<(), ProcessGroupError> {
66        use crate::tasks::process::group::builder::SendHandle;
67        if let Some(SendHandle(job_handle)) = &self.inner.job_handle {
68            unsafe {
69                use windows::Win32::System::JobObjects::TerminateJobObject;
70                // Terminate all processes in the job object
71                // Note: Do NOT call CloseHandle here - the Drop implementation will handle it
72                // Calling CloseHandle here would cause a double-free when Drop is called
73                TerminateJobObject(*job_handle, 1).map_err(|e| {
74                    ProcessGroupError::SignalFailed(format!(
75                        "Failed to terminate job object: {}",
76                        e
77                    ))
78                })?;
79            }
80            Ok(())
81        } else {
82            Err(ProcessGroupError::SignalFailed(
83                "No Job Object handle available".to_string(),
84            ))
85        }
86    }
87
88    /// Process group termination is not available on this platform.
89    #[cfg(not(any(unix, windows)))]
90    pub fn stop_group(&self) -> Result<(), ProcessGroupError> {
91        Err(ProcessGroupError::UnsupportedPlatform(
92            "Process group termination not available on this platform".to_string(),
93        ))
94    }
95}