killport/
killport.rs

1use crate::docker::DockerContainer;
2#[cfg(target_os = "linux")]
3use crate::linux::find_target_processes;
4#[cfg(target_os = "macos")]
5use crate::macos::find_target_processes;
6#[cfg(target_os = "windows")]
7use crate::windows::find_target_processes;
8use crate::{cli::Mode, signal::KillportSignal};
9use std::{fmt::Display, io::Error};
10
11/// Interface for killable targets such as native process and docker container.
12pub trait Killable {
13    fn kill(&self, signal: KillportSignal) -> Result<bool, Error>;
14
15    fn get_type(&self) -> KillableType;
16
17    fn get_name(&self) -> String;
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum KillableType {
22    Process,
23    Container,
24}
25
26impl Display for KillableType {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.write_str(match self {
29            KillableType::Process => "process",
30            KillableType::Container => "container",
31        })
32    }
33}
34
35impl Killable for DockerContainer {
36    /// Entry point to kill the docker containers.
37    ///
38    /// # Arguments
39    ///
40    /// * `signal` - A enum value representing the signal type.
41    fn kill(&self, signal: KillportSignal) -> Result<bool, Error> {
42        Self::kill_container(&self.name, signal)?;
43
44        Ok(true)
45    }
46
47    /// Returns the type of the killable target.
48    ///
49    /// This method is used to identify the type of the target (either a native process or a Docker container)
50    /// that is being handled. This information can be useful for logging, error handling, or other needs
51    /// where type of the target is relevant.
52    ///
53    /// # Returns
54    ///
55    /// * `String` - A string that describes the type of the killable target. For a `UnixProcess` it will return "process",
56    /// and for a `DockerContainer` it will return "container".
57    fn get_type(&self) -> KillableType {
58        KillableType::Container
59    }
60
61    fn get_name(&self) -> String {
62        self.name.to_string()
63    }
64}
65
66pub trait KillportOperations {
67    /// Finds the killables (native processes and docker containers) associated with the specified `port`.
68    fn find_target_killables(&self, port: u16, mode: Mode)
69        -> Result<Vec<Box<dyn Killable>>, Error>;
70
71    /// Manages the action of killing or simulating the killing of services by port.
72    fn kill_service_by_port(
73        &self,
74        port: u16,
75        signal: KillportSignal,
76        mode: Mode,
77        dry_run: bool,
78    ) -> Result<Vec<(KillableType, String)>, Error>;
79}
80
81pub struct Killport;
82
83impl KillportOperations for Killport {
84    /// Finds the killables (native processes and docker containers) associated with the specified `port`.
85    ///
86    /// Returns a `Vec` of killables.
87    ///
88    /// # Arguments
89    ///
90    /// * `port` - A u16 value representing the port number.
91    fn find_target_killables(
92        &self,
93        port: u16,
94        mode: Mode,
95    ) -> Result<Vec<Box<dyn Killable>>, Error> {
96        let mut target_killables: Vec<Box<dyn Killable>> = vec![];
97        let docker_present = mode != Mode::Process && DockerContainer::is_docker_present()?;
98
99        if mode != Mode::Container {
100            let target_processes = find_target_processes(port)?;
101
102            for process in target_processes {
103                // Check if the process name contains 'docker' and skip if in docker mode
104                if docker_present && process.get_name().to_lowercase().contains("docker") {
105                    continue;
106                }
107
108                target_killables.push(Box::new(process));
109            }
110        }
111
112        // Add containers if Docker is present and mode is not set to only process
113        if docker_present && mode != Mode::Process {
114            let target_containers = DockerContainer::find_target_containers(port)?; // Assume this function returns Result<Vec<DockerContainer>, Error>
115
116            for container in target_containers {
117                target_killables.push(Box::new(container));
118            }
119        }
120
121        Ok(target_killables)
122    }
123
124    /// Manages the action of killing or simulating the killing of services by port.
125    /// This function can either actually kill processes or containers, or simulate the action based on the `dry_run` flag.
126    ///
127    /// # Arguments
128    /// * `port` - The port number to check for killable entities.
129    /// * `signal` - The signal to send if not simulating.
130    /// * `mode` - The mode of operation, determining if processes, containers, or both should be targeted.
131    /// * `dry_run` - If true, simulates the actions without actually killing any entities.
132    ///
133    /// # Returns
134    /// * `Result<Vec<(String, String)>, Error>` - A list of killable entities or an error.
135    fn kill_service_by_port(
136        &self,
137        port: u16,
138        signal: KillportSignal,
139        mode: Mode,
140        dry_run: bool,
141    ) -> Result<Vec<(KillableType, String)>, Error> {
142        let mut results = Vec::new();
143        let target_killables = self.find_target_killables(port, mode)?; // Use the existing function to find targets
144
145        for killable in target_killables {
146            if dry_run {
147                // In dry-run mode, collect information about the entity without killing
148                results.push((killable.get_type(), killable.get_name()));
149            } else {
150                // In actual mode, attempt to kill the entity and collect its information if successful
151                if killable.kill(signal.clone())? {
152                    results.push((killable.get_type(), killable.get_name()));
153                }
154            }
155        }
156
157        Ok(results)
158    }
159}