diff --git a/files_darwin.go b/files_darwin.go new file mode 100644 index 0000000..9f14a43 --- /dev/null +++ b/files_darwin.go @@ -0,0 +1,21 @@ +//+build darwin + +package staros + +import ( + "os" + "syscall" + "time" +) + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func GetFileCreationTime(fileinfo os.FileInfo) time.Time { + return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Ctimespec) +} + +func GetFileAccessTime(fileinfo os.FileInfo) time.Time { + return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atimespec) +} diff --git a/files_unix.go b/files_unix.go index beb6b8c..51a6cdb 100644 --- a/files_unix.go +++ b/files_unix.go @@ -1,4 +1,4 @@ -//+build linux darwin +//+build linux package staros @@ -18,4 +18,4 @@ func GetFileCreationTime(fileinfo os.FileInfo) time.Time { func GetFileAccessTime(fileinfo os.FileInfo) time.Time { return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atim) -} \ No newline at end of file +} diff --git a/files_windows.go b/files_windows.go index 63fb604..b76b2f5 100644 --- a/files_windows.go +++ b/files_windows.go @@ -23,5 +23,5 @@ func SetFileTimes(file *os.File,info os.FileInfo) { } func SetFileTimesbyTime(file *os.File) { - syscall.SetFileTime() + } \ No newline at end of file diff --git a/memory_darwin.go b/memory_darwin.go index 268d6d8..9bb85d5 100644 --- a/memory_darwin.go +++ b/memory_darwin.go @@ -2,7 +2,100 @@ package staros +import ( + "encoding/binary" + "fmt" + "golang.org/x/sys/unix" + "os/exec" + "strconv" + "strings" + "unsafe" +) + // Memory 系统内存信息 -func Memory() MemStatus { - return MemStatus{} +func Memory() (MemStatus,error) { + return darwinMemory() +} + +type swapUsage struct { + Total uint64 + Avail uint64 + Used uint64 + Pagesize int32 + Encrypted bool +} + +func darwinMemory() (MemStatus, error) { + var err error + var res MemStatus + vm_stat, err := exec.LookPath("vm_stat") + if err != nil { + return res, err + } + out, err := exec.Command(vm_stat).CombinedOutput() + if err != nil { + return res, err + } + totalString, err := unix.Sysctl("hw.memsize") + if err != nil { + return res, err + } + + // unix.sysctl() helpfully assumes the result is a null-terminated string and + // removes the last byte of the result if it's 0 :/ + totalString += "\x00" + + res.All = uint64(binary.LittleEndian.Uint64([]byte(totalString))) + + lines := strings.Split(string(out), "\n") + pagesize := uint64(unix.Getpagesize()) + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.Trim(fields[1], " .") + switch key { + case "Pages free": + free, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + res.Free = free * pagesize + case "Pages inactive": + inactive, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + res.Available = inactive * pagesize + case "Pages active": + active, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + _ = active * pagesize + case "Pages wired down": + wired, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + _ = wired * pagesize + } + } + res.Available += res.Free + res.Used = res.All - res.Available + //swap + value, err := unix.SysctlRaw("vm.swapusage") + if err != nil { + return res, err + } + if len(value) != 32 { + return res, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value)) + } + swap := (*swapUsage)(unsafe.Pointer(&value[0])) + res.SwapAll = swap.Total + res.SwapUsed = swap.Used + res.SwapFree = swap.Avail + return res, err } diff --git a/memory_unix.go b/memory_unix.go index 4b5789b..e77a682 100644 --- a/memory_unix.go +++ b/memory_unix.go @@ -5,11 +5,11 @@ package staros import "syscall" // Memory 系统内存信息 -func Memory() MemStatus { +func Memory() (MemStatus, error) { var mem MemStatus ram := new(syscall.Sysinfo_t) if err := syscall.Sysinfo(ram); err != nil { - return mem + return mem, err } mem.All = uint64(ram.Totalram) mem.BuffCache = uint64(ram.Bufferram) @@ -20,5 +20,5 @@ func Memory() MemStatus { mem.SwapFree = uint64(ram.Freeswap) mem.SwapUsed = uint64(mem.SwapAll - mem.SwapFree) mem.Used = uint64(mem.All - mem.Free) - return mem + return mem, nil } diff --git a/memory_windows.go b/memory_windows.go new file mode 100644 index 0000000..5995975 --- /dev/null +++ b/memory_windows.go @@ -0,0 +1,26 @@ +// +build windows + +package staros + +import "b612.me/win32api" + +// Memory 系统内存信息 +func Memory() (MemStatus, error) { + var mem MemStatus + ram := new(win32api.MEMORYSTATUSEX) + _, err := win32api.GlobalMemoryStatusEx(ram) + if err != nil { + return mem, err + } + mem.All = uint64(ram.UllTotalPhys) + mem.Free = uint64(ram.UllAvailPhys) + mem.Available = uint64(ram.UllAvailPhys) + mem.Used = uint64(mem.All - mem.Free) + mem.SwapAll = uint64(ram.UllTotalPageFile) + mem.SwapFree = uint64(ram.UllAvailPageFile) + mem.SwapUsed = mem.SwapAll - mem.SwapFree + mem.VirtualAll = uint64(mem.VirtualAll) + mem.VirtualAvail = uint64(mem.VirtualAvail) + mem.VirtualUsed = mem.VirtualAll - mem.VirtualUsed + return mem, nil +} diff --git a/network.go b/network.go deleted file mode 100644 index 815f4f7..0000000 --- a/network.go +++ /dev/null @@ -1,85 +0,0 @@ -// +build !windows - -package staros - -import ( - "errors" - "io/ioutil" - "strconv" - "strings" - "time" -) - -func NetUsage() ([]NetAdapter, error) { - data, err := ioutil.ReadFile("/proc/net/dev") - if err != nil { - return []NetAdapter{}, err - } - sps := strings.Split(strings.TrimSpace(string(data)), "\n") - if len(sps) < 3 { - return []NetAdapter{}, errors.New("No Adaptor") - } - var res []NetAdapter - netLists := sps[2:] - for _, v := range netLists { - v = strings.ReplaceAll(v, " ", " ") - for strings.Contains(v, " ") { - v = strings.ReplaceAll(v, " ", " ") - } - v = strings.TrimSpace(v) - card := strings.Split(v, " ") - name := strings.ReplaceAll(card[0], ":", "") - recvBytes, _ := strconv.Atoi(card[1]) - sendBytes, _ := strconv.Atoi(card[9]) - res = append(res, NetAdapter{name, uint64(recvBytes), uint64(sendBytes)}) - } - return res, nil -} - -func NetUsageByname(name string) (NetAdapter, error) { - ada, err := NetUsage() - if err != nil { - return NetAdapter{}, err - } - for _, v := range ada { - if v.Name == name { - return v, nil - } - } - return NetAdapter{}, errors.New("Not Found") -} - -func NetSpeeds(duration time.Duration) ([]NetSpeed, error) { - list1, err := NetUsage() - if err != nil { - return []NetSpeed{}, err - } - time.Sleep(duration) - list2, err := NetUsage() - if err != nil { - return []NetSpeed{}, err - } - if len(list1) > len(list2) { - return []NetSpeed{}, errors.New("NetWork Adaptor Num Not ok") - } - var res []NetSpeed - for k, v := range list1 { - recv := float64(list2[k].RecvBytes-v.RecvBytes) / duration.Seconds() - send := float64(list2[k].SendBytes-v.SendBytes) / duration.Seconds() - res = append(res, NetSpeed{v.Name, recv, send}) - } - return res, nil -} - -func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) { - ada, err := NetSpeeds(duration) - if err != nil { - return NetSpeed{}, err - } - for _, v := range ada { - if v.Name == name { - return v, nil - } - } - return NetSpeed{}, errors.New("Not Found") -} diff --git a/network_test.go b/network_test.go new file mode 100644 index 0000000..1b30d7f --- /dev/null +++ b/network_test.go @@ -0,0 +1,9 @@ +package staros + +import ( + "testing" +) + +func Test_TrimSpace(t *testing.T) { + +} diff --git a/network_unix.go b/network_unix.go new file mode 100644 index 0000000..177cbb2 --- /dev/null +++ b/network_unix.go @@ -0,0 +1,248 @@ +// +build !windows + +package staros + +import ( + "errors" + "io/ioutil" + "os" + "strconv" + "strings" + "time" +) + +func NetUsage() ([]NetAdapter, error) { + data, err := ioutil.ReadFile("/proc/net/dev") + if err != nil { + return []NetAdapter{}, err + } + sps := strings.Split(strings.TrimSpace(string(data)), "\n") + if len(sps) < 3 { + return []NetAdapter{}, errors.New("No Adaptor") + } + var res []NetAdapter + netLists := sps[2:] + for _, v := range netLists { + v = strings.ReplaceAll(v, " ", " ") + for strings.Contains(v, " ") { + v = strings.ReplaceAll(v, " ", " ") + } + v = strings.TrimSpace(v) + card := strings.Split(v, " ") + name := strings.ReplaceAll(card[0], ":", "") + recvBytes, _ := strconv.Atoi(card[1]) + sendBytes, _ := strconv.Atoi(card[9]) + res = append(res, NetAdapter{name, uint64(recvBytes), uint64(sendBytes)}) + } + return res, nil +} + +func NetUsageByname(name string) (NetAdapter, error) { + ada, err := NetUsage() + if err != nil { + return NetAdapter{}, err + } + for _, v := range ada { + if v.Name == name { + return v, nil + } + } + return NetAdapter{}, errors.New("Not Found") +} + +func NetSpeeds(duration time.Duration) ([]NetSpeed, error) { + list1, err := NetUsage() + if err != nil { + return []NetSpeed{}, err + } + time.Sleep(duration) + list2, err := NetUsage() + if err != nil { + return []NetSpeed{}, err + } + if len(list1) > len(list2) { + return []NetSpeed{}, errors.New("NetWork Adaptor Num Not ok") + } + var res []NetSpeed + for k, v := range list1 { + recv := float64(list2[k].RecvBytes-v.RecvBytes) / duration.Seconds() + send := float64(list2[k].SendBytes-v.SendBytes) / duration.Seconds() + res = append(res, NetSpeed{v.Name, recv, send}) + } + return res, nil +} + +func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) { + ada, err := NetSpeeds(duration) + if err != nil { + return NetSpeed{}, err + } + for _, v := range ada { + if v.Name == name { + return v, nil + } + } + return NetSpeed{}, errors.New("Not Found") +} + +// NetConnections return all TCP/UDP/UNIX DOMAIN SOCKET Connections +// if your uid != 0 ,and analysePid==true ,you should have CAP_SYS_PRTACE and CAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCH Caps +func NetConnections(analysePid bool) ([]NetConn, error) { + var result []NetConn + var inodeMap map[string]int64 + var err error + fileList := []string{ + "/proc/net/tcp", + "/proc/net/tcp6", + "/proc/net/udp", + "/proc/net/udp6", + "/proc/net/unix", + } + if analysePid { + inodeMap, err = GetInodeMap() + if err != nil { + return result, err + } + } + for _, file := range fileList { + data, err := ioutil.ReadFile(file) + if err != nil { + return result, err + } + tmpRes, err := analyseNetFiles(data, inodeMap, file[strings.LastIndex(file, "/")+1:]) + if err != nil { + return result, err + } + result = append(result, tmpRes...) + } + return result, nil +} + +func GetInodeMap() (map[string]int64, error) { + res := make(map[string]int64) + paths, err := ioutil.ReadDir("/proc") + if err != nil { + return nil, err + } + for _, v := range paths { + if v.IsDir() && Exists("/proc/"+v.Name()+"/fd") { + fds, err := ioutil.ReadDir("/proc/" + v.Name() + "/fd") + if err != nil && Exists("/proc/"+v.Name()+"/fd") { + return nil, err + } + for _, fd := range fds { + socket, err := os.Readlink("/proc/" + v.Name() + "/fd/" + fd.Name()) + if err != nil { + continue + } + start := strings.Index(socket, "[") + if start < 0 { + continue + } + pid, err := strconv.ParseInt(v.Name(), 10, 64) + if err != nil { + break + } + res[socket[start+1:len(socket)-1]] = pid + } + } + } + return nil, err +} + +func analyseNetFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) { + if typed == "unix" { + return analyseUnixFiles(data, inodeMap, typed) + } + var result []NetConn + strdata := strings.TrimSpace(string(data)) + strdata = remainOne(strdata, " ", " ") + csvData := strings.Split(strdata, "\n") + pidMap := make(map[int64]*Process) + for line, lineData := range csvData { + if line == 0 { + continue + } + v := strings.Split(strings.TrimSpace(lineData), " ") + var res NetConn + ip, port, err := parseHexIpPort(v[1]) + if err != nil { + return result, err + } + res.LocalAddr = ip + res.LocalPort = port + ip, port, err = parseHexIpPort(v[2]) + if err != nil { + return result, err + } + res.RemoteAddr = ip + res.RemotePort = port + res.Uid, err = strconv.ParseInt(v[7], 10, 64) + if err != nil { + return result, err + } + res.Inode = v[9] + if inodeMap != nil && len(inodeMap) > 0 { + var ok bool + res.Pid, ok = inodeMap[res.Inode] + if !ok { + res.Pid = -1 + } else { + _, ok := pidMap[res.Pid] + if !ok { + tmp, err := FindProcessByPid(res.Pid) + if err != nil { + pidMap[res.Pid] = nil + } else { + pidMap[res.Pid] = &tmp + } + } + res.Process = pidMap[res.Pid] + } + } + res.Typed = typed + result = append(result, res) + } + return result, nil +} + +func analyseUnixFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) { + var result []NetConn + strdata := strings.TrimSpace(string(data)) + strdata = remainOne(strdata, " ", " ") + csvData := strings.Split(strdata, "\n") + pidMap := make(map[int64]*Process) + for line, lineData := range csvData { + if line == 0 { + continue + } + v := strings.Split(strings.TrimSpace(lineData), " ") + var res NetConn + res.Inode = v[6] + if len(v) == 8 { + res.Socket = v[7] + } + if inodeMap != nil && len(inodeMap) > 0 { + var ok bool + res.Pid, ok = inodeMap[res.Inode] + if !ok { + res.Pid = -1 + } else { + _, ok := pidMap[res.Pid] + if !ok { + tmp, err := FindProcessByPid(res.Pid) + if err != nil { + pidMap[res.Pid] = nil + } else { + pidMap[res.Pid] = &tmp + } + } + res.Uid = int64(pidMap[res.Pid].RUID) + res.Process = pidMap[res.Pid] + } + } + res.Typed = typed + result = append(result, res) + } + return result, nil +} diff --git a/network_windows.go b/network_windows.go index 12578a3..1bf985c 100644 --- a/network_windows.go +++ b/network_windows.go @@ -24,3 +24,10 @@ func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) { return NetSpeed{}, nil } + +// NetConnections return all TCP/UDP/UNIX DOMAIN SOCKET Connections +// if your uid != 0 ,and analysePid==true ,you should have CAP_SYS_PRTACE and CAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCH Caps +func NetConnections(analysePid bool) ([]NetConn, error) { + var result []NetConn + return result, nil +} \ No newline at end of file diff --git a/os_unix.go b/os_unix.go index 7333aff..73e0f9a 100644 --- a/os_unix.go +++ b/os_unix.go @@ -1,4 +1,4 @@ -// +build !windows +// +build linux darwin unix package staros diff --git a/os_windows.go b/os_windows.go index 69c90fc..128abb2 100644 --- a/os_windows.go +++ b/os_windows.go @@ -27,23 +27,6 @@ func IsRoot() bool { return wincmd.Isas() } -// Memory 系统内存信息 -func Memory() MemStatus { - var mem MemStatus - ram := new(win32api.MEMORYSTATUSEX) - win32api.GlobalMemoryStatusEx(ram) - mem.All = uint64(ram.UllTotalPhys) - mem.Free = uint64(ram.UllAvailPhys) - mem.Available = uint64(ram.UllAvailPhys) - mem.Used = uint64(mem.All - mem.Free) - mem.SwapAll = uint64(ram.UllTotalPageFile) - mem.SwapFree = uint64(ram.UllAvailPageFile) - mem.SwapUsed = mem.SwapAll - mem.SwapFree - mem.VirtualAll = uint64(mem.VirtualAll) - mem.VirtualAvail = uint64(mem.VirtualAvail) - mem.VirtualUsed = mem.VirtualAll - mem.VirtualUsed - return mem -} func DiskUsage(path string) (disk DiskStatus) { kernel32, err := syscall.LoadLibrary("Kernel32.dll") diff --git a/process.go b/process.go index 12916b9..11bdef4 100644 --- a/process.go +++ b/process.go @@ -7,6 +7,8 @@ import ( "io" "os" "os/exec" + "strings" + "sync" "syscall" "time" ) @@ -31,6 +33,7 @@ type StarCmd struct { stderrBuf *bytes.Buffer stdoutpoint int stderrpoint int + lock sync.Mutex prewrite []string prewritetime time.Duration stopctxfunc context.CancelFunc @@ -98,12 +101,13 @@ func (starcli *StarCmd) queryStdout(ctx context.Context) { case <-ctx.Done(): return default: - break } out := make([]byte, 65535) n, err := starcli.outfile.Read(out) if n != 0 { + starcli.lock.Lock() starcli.stdoutBuf.Write(out[:n]) + starcli.lock.Unlock() for _, v := range out[:n] { starcli.stdout = append(starcli.stdout, v) } @@ -112,7 +116,9 @@ func (starcli *StarCmd) queryStdout(ctx context.Context) { if err == io.EOF { break } else { - starcli.runerr = err + if !strings.Contains(err.Error(),"file already closed") { + starcli.runerr = err + } return } } @@ -125,12 +131,13 @@ func (starcli *StarCmd) queryStderr(ctx context.Context) { case <-ctx.Done(): return default: - break } out := make([]byte, 65535) n, err := starcli.errfile.Read(out) if n != 0 { + starcli.lock.Lock() starcli.stderrBuf.Write(out[:n]) + starcli.lock.Unlock() for _, v := range out[:n] { starcli.errout = append(starcli.errout, v) } @@ -139,7 +146,9 @@ func (starcli *StarCmd) queryStderr(ctx context.Context) { if err == io.EOF { break } else { - starcli.runerr = err + if !strings.Contains(err.Error(),"file already closed") { + starcli.runerr = err + } return } } @@ -147,8 +156,10 @@ func (starcli *StarCmd) queryStderr(ctx context.Context) { return } func (starcli *StarCmd) NowLineOutput() (string, error) { + starcli.lock.Lock() buf, _ := starcli.stdoutBuf.ReadBytes('\n') buferr, _ := starcli.stderrBuf.ReadBytes(byte('\n')) + starcli.lock.Unlock() if len(buferr) != 0 { return string(buf), errors.New(string(buferr)) } @@ -156,11 +167,15 @@ func (starcli *StarCmd) NowLineOutput() (string, error) { } func (starcli *StarCmd) NowLineStdOut() string { + starcli.lock.Lock() + defer starcli.lock.Unlock() buf, _ := starcli.stdoutBuf.ReadBytes('\n') return string(buf) } func (starcli *StarCmd) NowLineStdErr() error { + starcli.lock.Lock() + defer starcli.lock.Unlock() buferr, _ := starcli.stderrBuf.ReadBytes(byte('\n')) if len(buferr) != 0 { return errors.New(string(buferr)) @@ -170,16 +185,20 @@ func (starcli *StarCmd) NowLineStdErr() error { func (starcli *StarCmd) NowAllOutput() (string, error) { var outstr string + starcli.lock.Lock() buf := make([]byte, starcli.stdoutBuf.Len()) n, _ := starcli.stdoutBuf.Read(buf) + starcli.lock.Unlock() if n != 0 { outstr = string(buf[:n]) } if starcli.runerr != nil { return outstr, starcli.runerr } + starcli.lock.Lock() buf = make([]byte, starcli.stderrBuf.Len()) n, _ = starcli.stderrBuf.Read(buf) + starcli.lock.Unlock() if n != 0 { return outstr, errors.New(string(buf[:n])) } @@ -188,8 +207,10 @@ func (starcli *StarCmd) NowAllOutput() (string, error) { func (starcli *StarCmd) NowStdOut() string { var outstr string + starcli.lock.Lock() buf := make([]byte, starcli.stdoutBuf.Len()) n, _ := starcli.stdoutBuf.Read(buf) + starcli.lock.Unlock() if n != 0 { outstr = string(buf[:n]) } @@ -197,9 +218,10 @@ func (starcli *StarCmd) NowStdOut() string { } func (starcli *StarCmd) NowStdErr() error { - + starcli.lock.Lock() buf := make([]byte, starcli.stderrBuf.Len()) n, _ := starcli.stderrBuf.Read(buf) + starcli.lock.Unlock() if n != 0 { return errors.New(string(buf[:n])) } diff --git a/process_unix.go b/process_unix.go index 9b3c8b0..1fd153c 100644 --- a/process_unix.go +++ b/process_unix.go @@ -30,11 +30,43 @@ func FindProcessByName(name string) (datas []Process, err error) { func FindProcess(compare func(Process) bool) (datas []Process, err error) { var name, main string var mainb []byte - paths, errs := ioutil.ReadDir("/proc") - if errs != nil { - err = errs + var netErr error + var netInfo []NetConn + paths, err := ioutil.ReadDir("/proc") + if err != nil { return } + netInfo, netErr = NetConnections(false) + appendNetInfo := func(p *Process) { + if netErr != nil { + p.netErr = netErr + return + } + fds, err := ioutil.ReadDir("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd") + if err != nil && Exists("/proc/"+strconv.Itoa(int(p.Pid))+"/fd") { + p.netErr = err + return + } + for _, fd := range fds { + socket, err := os.Readlink("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd/" + fd.Name()) + if err != nil { + p.netErr = err + return + } + start := strings.Index(socket, "[") + if start < 0 { + continue + } + sid := socket[start+1 : len(socket)-1] + for _, v := range netInfo { + if v.Inode == sid { + v.Pid = p.Pid + v.Process = p + p.netConn = append(p.netConn, v) + } + } + } + } for _, v := range paths { if v.IsDir() && Exists("/proc/"+v.Name()+"/comm") { name, err = readAsString("/proc/" + v.Name() + "/comm") @@ -49,68 +81,80 @@ func FindProcess(compare func(Process) bool) (datas []Process, err error) { tmp.Name = strings.TrimSpace(name) main, err = readAsString("/proc/" + v.Name() + "/status") if err != nil { + tmp.Err = err if compare(tmp) { + appendNetInfo(&tmp) datas = append(datas, tmp) continue } + } else { + data := splitBy(main, ":") + tmp.Pid, _ = strconv.ParseInt(data["Pid"], 10, 64) + tmp.PPid, _ = strconv.ParseInt(data["PPid"], 10, 64) + tmp.TPid, _ = strconv.ParseInt(data["TracerPid"], 10, 64) + uids := splitBySpace(data["Uid"]) + gids := splitBySpace(data["Gid"]) + tmp.RUID, _ = strconv.Atoi(uids[0]) + tmp.EUID, _ = strconv.Atoi(uids[1]) + tmp.RGID, _ = strconv.Atoi(gids[0]) + tmp.EGID, _ = strconv.Atoi(gids[1]) + tmp.VmPeak, _ = strconv.ParseInt(splitBySpace(data["VmPeak"])[0], 10, 64) + tmp.VmSize, _ = strconv.ParseInt(splitBySpace(data["VmSize"])[0], 10, 64) + tmp.VmHWM, _ = strconv.ParseInt(splitBySpace(data["VmHWM"])[0], 10, 64) + tmp.VmRSS, _ = strconv.ParseInt(splitBySpace(data["VmRSS"])[0], 10, 64) + tmp.VmLck, _ = strconv.ParseInt(splitBySpace(data["VmLck"])[0], 10, 64) + tmp.VmData, _ = strconv.ParseInt(splitBySpace(data["VmData"])[0], 10, 64) + tmp.VmLck *= 1024 + tmp.VmData *= 1024 + tmp.VmPeak *= 1024 + tmp.VmSize *= 1024 + tmp.VmHWM *= 1024 + tmp.VmRSS *= 1024 } - data := splitBy(main, ":") - tmp.Pid, _ = strconv.ParseInt(data["Pid"], 10, 64) - tmp.PPid, _ = strconv.ParseInt(data["PPid"], 10, 64) - tmp.TPid, _ = strconv.ParseInt(data["TracerPid"], 10, 64) - uids := splitBySpace(data["Uid"]) - gids := splitBySpace(data["Gid"]) - tmp.RUID, _ = strconv.Atoi(uids[0]) - tmp.EUID, _ = strconv.Atoi(uids[1]) - tmp.RGID, _ = strconv.Atoi(gids[0]) - tmp.EGID, _ = strconv.Atoi(gids[1]) - tmp.VmPeak, _ = strconv.ParseInt(splitBySpace(data["VmPeak"])[0], 10, 64) - tmp.VmSize, _ = strconv.ParseInt(splitBySpace(data["VmSize"])[0], 10, 64) - tmp.VmHWM, _ = strconv.ParseInt(splitBySpace(data["VmHWM"])[0], 10, 64) - tmp.VmRSS, _ = strconv.ParseInt(splitBySpace(data["VmRSS"])[0], 10, 64) - tmp.VmLck, _ = strconv.ParseInt(splitBySpace(data["VmLck"])[0], 10, 64) - tmp.VmData, _ = strconv.ParseInt(splitBySpace(data["VmData"])[0], 10, 64) - tmp.VmLck *= 1024 - tmp.VmData *= 1024 - tmp.VmPeak *= 1024 - tmp.VmSize *= 1024 - tmp.VmHWM *= 1024 - tmp.VmRSS *= 1024 mainb, err = ioutil.ReadFile("/proc/" + v.Name() + "/cmdline") if err != nil { + tmp.Err = err if compare(tmp) { + appendNetInfo(&tmp) datas = append(datas, tmp) continue } + } else { + args := bytes.Split(mainb, []byte{0}) + for _, v := range args { + tmp.Args = append(tmp.Args, string(v)) + } } - args := bytes.Split(mainb, []byte{0}) - for _, v := range args { - tmp.Args = append(tmp.Args, string(v)) - } - mainb, err = ioutil.ReadFile("/proc/" + v.Name() + "/environ") if err != nil { + tmp.Err = err if compare(tmp) { + appendNetInfo(&tmp) datas = append(datas, tmp) continue } - } - args = bytes.Split(mainb, []byte{0}) - for _, v := range args { - tmp.Env = append(tmp.Env, string(v)) + } else { + args := bytes.Split(mainb, []byte{0}) + for _, v := range args { + tmp.Env = append(tmp.Env, string(v)) + } } main, err = readAsString("/proc/" + v.Name() + "/stat") if err != nil { + tmp.Err = err if compare(tmp) { + appendNetInfo(&tmp) datas = append(datas, tmp) continue } + } else { + times := splitBySpace(main) + uptime, _ := strconv.ParseInt(strings.TrimSpace(times[21]), 10, 64) + tmp.Uptime = time.Unix(StartTime().Unix()+uptime/100, int64((float64(uptime)/100-float64(uptime/100))*1000000000)) } - times := splitBySpace(main) - uptime, _ := strconv.ParseInt(strings.TrimSpace(times[21]), 10, 64) - tmp.Uptime = time.Unix(StartTime().Unix()+uptime/100, int64((float64(uptime)/100-float64(uptime/100))*1000000000)) if compare(tmp) { + appendNetInfo(&tmp) datas = append(datas, tmp) } } @@ -126,6 +170,38 @@ func FindProcessByPid(pid int64) (datas Process, err error) { err = errors.New("Not Found") return } + netInfo, netErr := NetConnections(false) + appendNetInfo := func(p *Process) { + if netErr != nil { + p.netErr = netErr + return + } + fds, err := ioutil.ReadDir("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd") + if err != nil && Exists("/proc/"+strconv.Itoa(int(p.Pid))+"/fd") { + p.netErr = err + return + } + for _, fd := range fds { + socket, err := os.Readlink("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd/" + fd.Name()) + if err != nil { + p.netErr = err + return + } + start := strings.Index(socket, "[") + if start < 0 { + continue + } + sid := socket[start+1 : len(socket)-1] + for _, v := range netInfo { + if v.Inode == sid { + v.Pid = p.Pid + v.Process = p + p.netConn = append(p.netConn, v) + } + } + } + } + name, err = readAsString("/proc/" + fmt.Sprint(pid) + "/comm") if err != nil { return @@ -157,22 +233,27 @@ func FindProcessByPid(pid int64) (datas Process, err error) { datas.VmSize *= 1024 datas.VmHWM *= 1024 datas.VmRSS *= 1024 + appendNetInfo(&datas) mainb, err = ioutil.ReadFile("/proc/" + fmt.Sprint(pid) + "/cmdline") if err != nil { - return - } - args := bytes.Split(mainb, []byte{0}) - for _, v := range args { - datas.Args = append(datas.Args, string(v)) + datas.Err = err + err = nil + } else { + args := bytes.Split(mainb, []byte{0}) + for _, v := range args { + datas.Args = append(datas.Args, string(v)) + } } mainb, err = ioutil.ReadFile("/proc/" + fmt.Sprint(pid) + "/environ") if err != nil { - return - } - args = bytes.Split(mainb, []byte{0}) - for _, v := range args { - datas.Env = append(datas.Env, string(v)) + datas.Err = err + err = nil + } else { + args := bytes.Split(mainb, []byte{0}) + for _, v := range args { + datas.Env = append(datas.Env, string(v)) + } } datas.LocalPath, err = os.Readlink("/proc/" + fmt.Sprint(pid) + "/exe") diff --git a/process_win.go b/process_win.go index 713741c..eba78db 100644 --- a/process_win.go +++ b/process_win.go @@ -62,3 +62,11 @@ func Daemon(path string, args ...string) (int, error) { func (starcli *StarCmd) SetRunUser(uid, gid uint32) { } + +func (starcli *StarCmd) Release() error { + if err := starcli.CMD.Start(); err != nil { + return err + } + starcli.CMD.Process.Release() + return nil +} \ No newline at end of file diff --git a/sysconf/sysconf.go b/sysconf/sysconf.go index 076856e..2b0cc97 100644 --- a/sysconf/sysconf.go +++ b/sysconf/sysconf.go @@ -9,8 +9,6 @@ import ( "strconv" "strings" "sync" - - "b612.me/staros" ) type SysConf struct { @@ -76,9 +74,6 @@ func NewLinuxConf(EqualFlag string) *SysConf { } func (syscfg *SysConf) ParseFromFile(filepath string) error { - if !staros.Exists(filepath) { - return errors.New(filepath + " 不存在") - } data, err := ioutil.ReadFile(filepath) if err != nil { return err diff --git a/tools.go b/tools.go index 8d265e5..1bfeeab 100644 --- a/tools.go +++ b/tools.go @@ -2,7 +2,9 @@ package staros import ( "bytes" + "errors" "io/ioutil" + "strconv" "strings" ) @@ -42,3 +44,116 @@ func readAsString(path string) (string, error) { } return string(data), nil } + +func remainOne(data, old, new string) string { + data = strings.TrimSpace(data) + if !strings.Contains(data, old) { + return data + } + data = strings.ReplaceAll(data, old, new) + return remainOne(data, old, new) +} + +func parseHexIpPort(str string) (string, int, error) { + str = strings.TrimSpace(str) + if len(str) != 13 && len(str) != 37 { + return "", 0, errors.New("Not a valid ip:port addr:" + str) + } + ipPort := strings.Split(str, ":") + if len(ipPort) != 2 { + return "", 0, errors.New("Not a valid ip:port addr:" + str) + } + if len(ipPort[0]) == 8 { + ip, err := parseHexIPv4(ipPort[0]) + if err != nil { + return "", 0, err + } + port, err := parseHexPort(ipPort[1]) + return ip, port, err + } + + if len(ipPort[0]) == 32 { + ip, err := parseHexIPv6(ipPort[0]) + if err != nil { + return "", 0, err + } + port, err := parseHexPort(ipPort[1]) + return ip, port, err + } + return "", 0, errors.New("Invalid ip address:" + str) +} + +func parseHexPort(str string) (int, error) { + tmpUint32, err := strconv.ParseUint(str, 16, 32) + return int(tmpUint32), err +} + +func parseHexIPv4(str string) (string, error) { + var result string + if len(str) != 8 { + return "", errors.New("Not a vaild ipv4:" + str) + } + tmpUint64, err := strconv.ParseUint(str, 16, 32) + if err != nil { + return "", err + } + numicIp := uint32(tmpUint64) + for i := 0; i < 4; i++ { + result += strconv.FormatUint(uint64(uint8(numicIp>>(8*uint8(i)))), 10) + "." + } + return result[0 : len(result)-1], nil +} + +func parseHexIPv6(str string) (string, error) { + var result string + if len(str) != 32 { + return "", errors.New("Not a vaild ipv6:" + str) + } + for i := 0; i < 4; i++ { + part := str[i*8 : (i+1)*8] + tmpUint64, err := strconv.ParseUint(part, 16, 32) + if err != nil { + return "", err + } + tmpUint32 := uint32(tmpUint64) + //07C2022A + for i := 0; i < 4; i++ { + tmp := strconv.FormatUint(uint64(uint8(tmpUint32>>uint8(8*i))), 16) + if len(tmp) == 1 { + tmp = "0" + tmp + } + result += tmp + if (i+1)%2 == 0 { + result += ":" + } + } + } + ipv6 := result[0 : len(result)-1] + ipv6List := strings.Split(ipv6, ":") + prepareZero := false + alreadyZero := false + for k, v := range ipv6List { + if v == "0000" && !alreadyZero { + ipv6List[k] = "" + prepareZero = true + continue + } + if v != "0000" && prepareZero { + alreadyZero = true + } + var nonZero = 0 + for i := 0; i < 4; i++ { + sig := v[i : i+1] + if sig != "0" { + nonZero = i + break + } + } + ipv6List[k] = v[nonZero:4] + } + ipv6 = strings.TrimSuffix(remainOne(strings.Join(ipv6List, ":"), ":::", "::"), "::") + if ipv6 == "" { + ipv6 = "::0" + } + return ipv6, nil +} diff --git a/typed.go b/typed.go index ef5e8f8..71a987b 100644 --- a/typed.go +++ b/typed.go @@ -46,6 +46,13 @@ type Process struct { VmHWM int64 VmRSS int64 VmData int64 + netConn []NetConn + netErr error + Err error +} + +func (p Process) GetNetConns() ([]NetConn, error) { + return p.netConn, p.netErr } type MemStatus struct { @@ -70,3 +77,16 @@ type DiskStatus struct { Free uint64 Available uint64 } + +type NetConn struct { + LocalAddr string + LocalPort int + Typed string + RemoteAddr string + RemotePort int + Socket string + Inode string + Pid int64 + Uid int64 + Process *Process +}