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}