1use std::io::{self, Error, ErrorKind};
2use std::net::TcpListener;
3use std::net::{IpAddr, Ipv4Addr, SocketAddr};
4use std::process::Command as ProcessCommand;
5
6pub fn port_available(port: u16, verbose: bool) -> bool {
11 let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
12
13 match TcpListener::bind(socket_addr) {
14 Ok(_) => {
15 if verbose {
16 println!("Port {} is available", port);
17 }
18 true
19 }
20 Err(_) => {
21 if verbose {
22 println!("Port {} is in use", port);
23 }
24 false
25 }
26 }
27}
28
29pub fn kill_port_process(port: u16, verbose: bool) {
34 #[cfg(target_os = "windows")]
35 let result = kill_port_process_windows(port, verbose);
36
37 #[cfg(not(target_os = "windows"))]
38 let result = kill_port_process_unix(port, verbose);
39
40 if let Err(e) = result {
41 if verbose {
42 eprintln!("Failed to kill process on port {}: {}", port, e);
43 }
44 }
45}
46
47#[cfg(not(target_os = "windows"))]
48fn kill_port_process_unix(port: u16, verbose: bool) -> io::Result<()> {
49 let lsof_output = ProcessCommand::new("lsof")
51 .args(["-i", &format!(":{}", port), "-t"])
52 .output()?;
53
54 if !lsof_output.status.success() {
55 return Err(Error::new(
56 ErrorKind::Other,
57 "Failed to execute lsof command",
58 ));
59 }
60
61 let pid_str = String::from_utf8_lossy(&lsof_output.stdout)
62 .trim()
63 .to_string();
64
65 if pid_str.is_empty() {
66 return Err(Error::new(
67 ErrorKind::Other,
68 format!("No process found using port {}", port),
69 ));
70 }
71
72 let kill_output = ProcessCommand::new("kill")
74 .arg("-9")
75 .arg(&pid_str)
76 .output()?;
77
78 if !kill_output.status.success() {
79 return Err(Error::new(
80 ErrorKind::Other,
81 format!("Failed to kill process with PID {}", pid_str),
82 ));
83 }
84
85 if verbose {
86 println!(
87 "Successfully killed process {} using port {}",
88 pid_str, port
89 );
90 }
91
92 Ok(())
93}
94
95#[cfg(target_os = "windows")]
96fn kill_port_process_windows(port: u16, verbose: bool) -> io::Result<()> {
97 let netstat_output = ProcessCommand::new("netstat")
99 .args(["-ano", "|", "findstr", &format!(":{}", port)])
100 .output()?;
101
102 if !netstat_output.status.success() {
103 return Err(Error::new(
104 ErrorKind::Other,
105 "Failed to execute netstat command",
106 ));
107 }
108
109 let output = String::from_utf8_lossy(&netstat_output.stdout);
110 let lines: Vec<&str> = output.lines().collect();
111
112 if lines.is_empty() {
113 return Err(Error::new(
114 ErrorKind::Other,
115 format!("No process found using port {}", port),
116 ));
117 }
118
119 let last_line = lines[0];
121 let parts: Vec<&str> = last_line.split_whitespace().collect();
122
123 if parts.len() < 5 {
124 return Err(Error::new(
125 ErrorKind::Other,
126 "Failed to parse netstat output",
127 ));
128 }
129
130 let pid_str = parts[4];
131
132 let taskkill_output = ProcessCommand::new("taskkill")
134 .args(["/F", "/PID", pid_str])
135 .output()?;
136
137 if !taskkill_output.status.success() {
138 return Err(Error::new(
139 ErrorKind::Other,
140 format!("Failed to kill process with PID {}", pid_str),
141 ));
142 }
143
144 if verbose {
145 println!(
146 "Successfully killed process {} using port {}",
147 pid_str, port
148 );
149 }
150
151 Ok(())
152}