2024-11-29 15:43:36 +00:00
|
|
|
package diagnostic
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os/exec"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DecodeLineFunc func(text string) (*Hop, error)
|
|
|
|
|
2024-12-03 12:56:28 +00:00
|
|
|
func decodeNetworkOutputToFile(command *exec.Cmd, decodeLine DecodeLineFunc) ([]*Hop, string, error) {
|
2024-11-29 15:43:36 +00:00
|
|
|
stdout, err := command.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", fmt.Errorf("error piping traceroute's output: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := command.Start(); err != nil {
|
|
|
|
return nil, "", fmt.Errorf("error starting traceroute: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tee the output to a string to have the raw information
|
|
|
|
// in case the decode call fails
|
|
|
|
// This error is handled only after the Wait call below returns
|
|
|
|
// otherwise the process can become a zombie
|
|
|
|
buf := bytes.NewBuffer([]byte{})
|
|
|
|
tee := io.TeeReader(stdout, buf)
|
2024-12-03 12:56:28 +00:00
|
|
|
hops, err := Decode(tee, decodeLine)
|
|
|
|
// regardless of success of the decoding
|
|
|
|
// consume all output to have available in buf
|
|
|
|
_, _ = io.ReadAll(tee)
|
2024-11-29 15:43:36 +00:00
|
|
|
|
|
|
|
if werr := command.Wait(); werr != nil {
|
|
|
|
return nil, "", fmt.Errorf("error finishing traceroute: %w", werr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, buf.String(), err
|
|
|
|
}
|
|
|
|
|
|
|
|
return hops, "", nil
|
|
|
|
}
|
|
|
|
|
2024-12-03 12:56:28 +00:00
|
|
|
func Decode(reader io.Reader, decodeLine DecodeLineFunc) ([]*Hop, error) {
|
2024-11-29 15:43:36 +00:00
|
|
|
scanner := bufio.NewScanner(reader)
|
|
|
|
scanner.Split(bufio.ScanLines)
|
|
|
|
|
|
|
|
var hops []*Hop
|
2024-12-03 12:56:28 +00:00
|
|
|
|
2024-11-29 15:43:36 +00:00
|
|
|
for scanner.Scan() {
|
|
|
|
text := scanner.Text()
|
2024-12-03 12:56:28 +00:00
|
|
|
if text == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
hop, err := decodeLine(text)
|
2024-11-29 15:43:36 +00:00
|
|
|
if err != nil {
|
2024-12-03 12:56:28 +00:00
|
|
|
// This continue is here on the error case because there are lines at the start and end
|
|
|
|
// that may not be parsable. (check windows tracert output)
|
|
|
|
// The skip is here because aside from the start and end lines the other lines should
|
|
|
|
// always be parsable without errors.
|
|
|
|
continue
|
2024-11-29 15:43:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hops = append(hops, hop)
|
|
|
|
}
|
|
|
|
|
|
|
|
if scanner.Err() != nil {
|
|
|
|
return nil, fmt.Errorf("scanner reported an error: %w", scanner.Err())
|
|
|
|
}
|
|
|
|
|
|
|
|
return hops, nil
|
|
|
|
}
|