package procspy // lsof-executing implementation. import ( "fmt" "strconv" "strings" ) var ( lsofFields = "cn" // parseLSOF() depends on the order ) // parseLsof parses lsof out with `-F cn` argument. // // Format description: the first letter is the type of record, records are // newline separated, the record starting with 'p' (pid) is a new processid. // There can be multiple connections for the same 'p' record in which case the // 'p' is not repeated. // // For example, this is one process with two listens and one connection: // // p13100 // cmpd // n[::1]:6600 // n127.0.0.1:6600 // n[::1]:6600->[::1]:50992 // func parseLSOF(out string) (map[string]Proc, error) { var ( res = map[string]Proc{} // Local addr -> Proc cp = Proc{} ) for _, line := range strings.Split(out, "\n") { if len(line) <= 1 { continue } var ( field = line[0] value = line[1:] ) switch field { case 'p': pid, err := strconv.Atoi(value) if err != nil { return nil, fmt.Errorf("invalid 'p' field in lsof output: %#v", value) } cp.PID = uint(pid) case 'n': // 'n' is the last field, with '-F cn' // format examples: // "192.168.2.111:44013->54.229.241.196:80" // "[2003:45:2b57:8900:1869:2947:f942:aba7]:55711->[2a00:1450:4008:c01::11]:443" // "*:111" <- a listen addresses := strings.SplitN(value, "->", 2) if len(addresses) != 2 { // That's a listen entry. continue } res[addresses[0]] = Proc{ Name: cp.Name, PID: cp.PID, } case 'c': cp.Name = value default: return nil, fmt.Errorf("unexpected lsof field: %c in %#v", field, value) } } return res, nil }