package main import ( "fmt" "io/ioutil" "net" "os" "path/filepath" "strings" "sync" ) func fmtAddress(node, port string) string { if isIPv6(node) { return fmt.Sprintf("[%s]:%s", node, port) } else { return fmt.Sprintf("%s:%s", node, port) } } func isIPv6(node string) bool { ip := net.ParseIP(node) return ip != nil && ip.To4() == nil } var wg sync.WaitGroup func Emit(pt Point) { if pt == nil { return } fmt.Println(pt) } func DoHostfile(fname string) { defer wg.Done() hostname := filepath.Base(fname) cfg, err := readConfigFile(fname) if err != nil { Emit(NewPoint("public", map[string]string{"host": hostname}, map[string]interface{}{"error": err.Error()})) return } for _, address := range getAddresses(cfg) { addressStr := fmtAddress(address.Node, address.Port) wg.Add(2) go Emit(DoAddress(hostname, "tcp4", addressStr)) go Emit(DoAddress(hostname, "tcp6", addressStr)) } } func DoAddress(host, network, address string) Point { defer wg.Done() tags := map[string]string{ "host": host, "network": network, "address": address, } addr, err := net.ResolveTCPAddr(network, address) if err != nil { if ae, ok := err.(*net.AddrError); ok && ae.Err == "no suitable address found" { return nil } return NewPoint("public", tags, map[string]interface{}{"error": err.Error()}) } var _wg sync.WaitGroup _wg.Add(2) var result_name string var result_version string var result_error error go func() { defer _wg.Done() result_name, result_version, result_error = Hello(addr) }() var result_ping float64 go func() { defer _wg.Done() result_ping = Ping(addr.IP) }() _wg.Wait() result := map[string]interface{}{} if result_error == nil { result["name"] = result_name result["version"] = result_version } else { result["error"] = result_error } if result_ping >= 0 { result["ping"] = result_ping } return NewPoint("public", tags, result) } func Hello(addr *net.TCPAddr) (name, version string, err error) { conn, err := net.DialTCP(addr.Network(), nil, addr) if err != nil { return "", "", err } defer conn.Close() conn.CloseWrite() all, _ := ioutil.ReadAll(conn) line := strings.TrimRight(string(all), "\n") parts := strings.Split(line, " ") if len(parts) != 3 { return "", "", fmt.Errorf("malformed ID line: %q", line) } return parts[1], parts[2], nil } func main() { for _, fname := range os.Args[1:] { wg.Add(1) go DoHostfile(fname) } wg.Wait() }