57 lines
1.3 KiB
Go
57 lines
1.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// pingDevice pings an IP address and returns the response time in milliseconds.
|
|
func pingDevice(ip string, timeoutMs int) (float64, error) {
|
|
parsedIP := net.ParseIP(ip)
|
|
if parsedIP == nil {
|
|
return 0, fmt.Errorf("invalid IP address: %s", ip)
|
|
}
|
|
|
|
pingCmd := "ping"
|
|
if parsedIP.To4() == nil {
|
|
pingCmd = "ping6"
|
|
}
|
|
|
|
timeoutSecs := max(1, timeoutMs/1000)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMs+1000)*time.Millisecond)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(ctx, pingCmd, "-c", "1", "-W", strconv.Itoa(timeoutSecs), ip)
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("ping failed: %s", strings.TrimSpace(string(output)))
|
|
}
|
|
|
|
return parsePingTime(string(output))
|
|
}
|
|
|
|
// parsePingTime extracts the response time from ping output.
|
|
func parsePingTime(output string) (float64, error) {
|
|
for _, line := range strings.Split(output, "\n") {
|
|
idx := strings.Index(line, "time=")
|
|
if idx < 0 {
|
|
continue
|
|
}
|
|
timeStr := line[idx+5:]
|
|
end := strings.Index(timeStr, " ms")
|
|
if end < 0 {
|
|
end = strings.IndexByte(timeStr, ' ')
|
|
}
|
|
if end < 0 {
|
|
end = len(timeStr)
|
|
}
|
|
return strconv.ParseFloat(timeStr[:end], 64)
|
|
}
|
|
return 0, fmt.Errorf("no time= field in ping output")
|
|
}
|