func main() { // Uncomment the following lines if you need to time the options parsing //log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) //log.Println(": Program started") // Configure Command Line Options var useUDP, quiet, debug bool var maxRTT time.Duration var numPing int getopt.BoolVarLong(&useUDP, "udp", 'u', "use UDP instead of ICMP") getopt.BoolVarLong(&quiet, "quiet", 'q', "only display host data") getopt.BoolVarLong(&debug, "debug", 'v', "print additional messages") maxRTT = defaultMaxRTT getopt.DurationVarLong(&maxRTT, "rtt", 't', "max RTT for each ping") numPing = defaultPingCount getopt.IntVarLong(&numPing, "count", 'n', "max number of pings per target") getopt.SetParameters("startIP endIP") getopt.Parse() // Verify arguments if getopt.NArgs() != 2 { log.Println("Incorrect number of arguments!") getopt.PrintUsage(os.Stderr) os.Exit(1) } startIPString := getopt.Arg(0) endIPString := getopt.Arg(1) // Test for incompatible options if quiet && debug { log.Println("`quiet` and `debug` are incompatible") getopt.PrintUsage(os.Stderr) os.Exit(1) } if debug { log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) log.Println(": Command Line Parsing complete") } // Convert to IP object startIP := net.ParseIP(startIPString) if startIP == nil { log.Fatal("Start IP,", startIPString, ", is not a valid IP address") } if debug { log.Println(": Start IP\t", startIPString) } endIP := net.ParseIP(endIPString) if endIP == nil { log.Fatal("End IP,", endIPString, ", is not a valid IP address") } if debug { log.Println(": End IP \t", endIPString) } netProto := "ip4:icmp" if strings.Index(startIPString, ":") != -1 { netProto = "ip6:ipv6-icmp" } p := fastping.NewPinger() p.MaxRTT = maxRTT p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) { var device resultData device.PingResult = addr.String() + "\t" + roundDuration(baseRTT+rtt, time.Millisecond).String() ips = append(ips, device) p.RemoveIPAddr(addr) } currentIP := make(net.IP, len(startIP)) for copy(currentIP, startIP); bytes.Compare(currentIP, endIP) <= 0; inc(currentIP) { ra, err := net.ResolveIPAddr(netProto, currentIP.String()) if err != nil { log.Fatal(err) } p.AddIPAddr(ra) } if useUDP { p.Network("udp") } if debug { log.Println(": Start Scan") } for index := 0; index < numPing; index++ { baseRTT = time.Duration(index) * maxRTT err := p.Run() if err != nil { log.Fatal("Pinger returns error: ", err) } } if debug { log.Println(": Scan complete") } if !quiet { fmt.Println() fmt.Printf("%d devices found\n", len(ips)) fmt.Println() } if debug { log.Println(": Start Host Lookup") } // Query DNS for the name of each device found by the ping scan var ipAndTime []string var hostname string var wg sync.WaitGroup for index, ip := range ips { ipAndTime = strings.SplitN(ip.PingResult, "\t", 2) wg.Add(1) go func(ipString string, localIndex int) { hosts, err := net.LookupAddr(ipString) if err != nil { hostname = "Error: " + err.Error() } else { hostname = strings.Join(hosts, ", ") } ips[localIndex].HostResult = hostname wg.Done() }(ipAndTime[0], index) } wg.Wait() if debug { log.Println(": DNS complete") } sort.Sort(byIP(ips)) if debug { log.Println(": Sort complete") } for _, ip := range ips { fmt.Printf("%-25s\t--> %s\n", ip.PingResult, ip.HostResult) } if !quiet { fmt.Println() } if debug { log.Println(": Program complete") } }
// main program func main() { // local variables var err error // parse commandline argiments getopt.Parse() // show help screen and exit in case of -h or --help option if *fHelp { getopt.Usage() os.Exit(1) } // look for mandatory positional arguments if getopt.NArgs() < 1 { log.Fatal("Nothing to do. Use -h for help.") } // by default, read server list from stdin ServerListFile := os.Stdin // read server names from file if a file name is supplied if *fFile != "" { ServerListFile, err = os.Open(*fFile) if err != nil { log.Fatal(fmt.Sprintf("ServerListFile: Error: %v", err)) } defer ServerListFile.Close() } AddrPadding, ServerList := LoadServerList(ServerListFile) // command to run on servers fCommand = getopt.Arg(0) // make new group group := &SshGroup{ Active: 0, Total: len(ServerList), Complete: 0, } // no point to display more processes than if *fProcs > group.Total { *fProcs = group.Total } // print heading text fmt.Fprintln(os.Stderr, "gssh - group ssh, ver. 0.6") fmt.Fprintln(os.Stderr, "(c)2014 Bozhin Zafirov <*****@*****.**>") fmt.Fprintln(os.Stderr) fmt.Fprintf(os.Stderr, " [*] read (%d) hosts from the list\n", group.Total) fmt.Fprintf(os.Stderr, " [*] executing '%s' as user '%s'\n", fCommand, *fUser) fmt.Fprintf(os.Stderr, " [*] spawning %d parallel ssh sessions\n\n", *fProcs) // spawn ssh processes for i, Server := range ServerList { ssh := &SshServer{ Username: *fUser, Address: Server, } group.Servers = append(group.Servers, ssh) // run command group.mu.Lock() group.Active++ group.mu.Unlock() go group.Command(ssh, AddrPadding, fCommand) // show progless after new process spawn group.UpdateProgress() if i < group.Total { // time delay and max procs wait between spawn time.Sleep(time.Duration(*fDelay) * time.Millisecond) group.Wait(*fProcs) } } // wait for ssh processes to exit group.Wait(0) group.mu.Lock() group.ClearProgress() group.mu.Unlock() // calculate stats var StdoutServersCount int var StderrServersCount int var AllServersCount int var StdoutLinesCount int var StderrLinesCount int var AllLinesCount int for _, ssh := range group.Servers { if ssh.StdoutLineCount > 0 { StdoutLinesCount += ssh.StdoutLineCount StdoutServersCount++ } if ssh.StderrLineCount > 0 { StderrLinesCount += ssh.StderrLineCount StderrServersCount++ } if ssh.StdoutLineCount > 0 || ssh.StderrLineCount > 0 { AllLinesCount += ssh.StdoutLineCount + ssh.StderrLineCount AllServersCount++ } } fmt.Fprintln(os.Stderr) fmt.Fprintf(os.Stderr, " Done. Processed: %d / Output: %d (%d) / \033[01;32m->\033[0m %d (%d) / \033[01;31m=>\033[0m %d (%d)\n", group.Total, AllServersCount, AllLinesCount, StdoutServersCount, StdoutLinesCount, StderrServersCount, StderrLinesCount, ) }