From a068ca150c7ef4844766de60b38a36d5536b9495 Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 15 Dec 2024 12:04:23 +0800 Subject: [PATCH] fix: missing package name on Android bare core, switch from netlink to procfs due to system restrictions. --- component/process/process_linux.go | 192 ++++------------------------- 1 file changed, 22 insertions(+), 170 deletions(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index 3ce45ae8..4153fdee 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -1,200 +1,52 @@ package process import ( - "bytes" - "encoding/binary" "fmt" + "github.com/shirou/gopsutil/v4/net" + "github.com/shirou/gopsutil/v4/process" "net/netip" - "os" - "path" - "path/filepath" - "runtime" - "strings" - "syscall" - "unicode" - "unsafe" - - "github.com/mdlayher/netlink" - "golang.org/x/sys/unix" ) -const ( - SOCK_DIAG_BY_FAMILY = 20 - inetDiagRequestSize = int(unsafe.Sizeof(inetDiagRequest{})) - inetDiagResponseSize = int(unsafe.Sizeof(inetDiagResponse{})) -) - -type inetDiagRequest struct { - Family byte - Protocol byte - Ext byte - Pad byte - States uint32 - - SrcPort [2]byte - DstPort [2]byte - Src [16]byte - Dst [16]byte - If uint32 - Cookie [2]uint32 -} - -type inetDiagResponse struct { - Family byte - State byte - Timer byte - ReTrans byte - - SrcPort [2]byte - DstPort [2]byte - Src [16]byte - Dst [16]byte - If uint32 - Cookie [2]uint32 - - Expires uint32 - RQueue uint32 - WQueue uint32 - UID uint32 - INode uint32 -} - func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) { - uid, inode, err := resolveSocketByNetlink(network, ip, srcPort) + uid, err := resolveSocketUID(ip, srcPort) if err != nil { return 0, "", err } - pp, err := resolveProcessNameByProcSearch(inode, uid) - return uid, pp, err + name, err := resolveProcessNameByUID(uid) + return uid, name, err } -func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) { - request := &inetDiagRequest{ - States: 0xffffffff, - Cookie: [2]uint32{0xffffffff, 0xffffffff}, - } - - if ip.Is4() { - request.Family = unix.AF_INET - } else { - request.Family = unix.AF_INET6 - } - - if strings.HasPrefix(network, "tcp") { - request.Protocol = unix.IPPROTO_TCP - } else if strings.HasPrefix(network, "udp") { - request.Protocol = unix.IPPROTO_UDP - } else { - return 0, 0, ErrInvalidNetwork - } - - copy(request.Src[:], ip.AsSlice()) - - binary.BigEndian.PutUint16(request.SrcPort[:], uint16(srcPort)) - - conn, err := netlink.Dial(unix.NETLINK_INET_DIAG, nil) +func resolveSocketUID(ip netip.Addr, srcPort int) (uint32, error) { + connections, err := net.Connections("all") if err != nil { - return 0, 0, err - } - defer conn.Close() - - message := netlink.Message{ - Header: netlink.Header{ - Type: SOCK_DIAG_BY_FAMILY, - Flags: netlink.Request | netlink.Dump, - }, - Data: (*(*[inetDiagRequestSize]byte)(unsafe.Pointer(request)))[:], + return 0, err } - messages, err := conn.Execute(message) - if err != nil { - return 0, 0, err - } - - for _, msg := range messages { - if len(msg.Data) < inetDiagResponseSize { - continue + for _, conn := range connections { + if conn.Laddr.Port == uint32(srcPort) && conn.Laddr.IP == ip.String() { + if len(conn.Uids) > 0 { + return uint32(conn.Uids[0]), nil + } } - - response := (*inetDiagResponse)(unsafe.Pointer(&msg.Data[0])) - - return response.UID, response.INode, nil } - - return 0, 0, ErrNotFound + return 0, ErrNotFound } -func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { - files, err := os.ReadDir("/proc") +func resolveProcessNameByUID(uid uint32) (string, error) { + processes, err := process.Processes() if err != nil { return "", err } - buffer := make([]byte, unix.PathMax) - socket := fmt.Appendf(nil, "socket:[%d]", inode) - - for _, f := range files { - if !f.IsDir() || !isPid(f.Name()) { - continue - } - - info, err := f.Info() - if err != nil { - return "", err - } - if info.Sys().(*syscall.Stat_t).Uid != uid { - continue - } - - processPath := filepath.Join("/proc", f.Name()) - fdPath := filepath.Join(processPath, "fd") - - fds, err := os.ReadDir(fdPath) - if err != nil { - continue - } - - for _, fd := range fds { - n, err := unix.Readlink(filepath.Join(fdPath, fd.Name()), buffer) + for _, p := range processes { + puid, err := p.Uids() + if err == nil && len(puid) > 0 && uint32(puid[0]) == uid { + name, err := p.Name() if err != nil { continue } - - if runtime.GOOS == "android" { - if bytes.Equal(buffer[:n], socket) { - cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) - if err != nil { - return "", err - } - - return splitCmdline(cmdline), nil - } - } else { - if bytes.Equal(buffer[:n], socket) { - return os.Readlink(filepath.Join(processPath, "exe")) - } - } + return name, nil } } - - return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode) -} - -func splitCmdline(cmdline []byte) string { - cmdline = bytes.Trim(cmdline, " ") - - idx := bytes.IndexFunc(cmdline, func(r rune) bool { - return unicode.IsControl(r) || unicode.IsSpace(r) - }) - - if idx == -1 { - return filepath.Base(string(cmdline)) - } - return filepath.Base(string(cmdline[:idx])) -} - -func isPid(s string) bool { - return strings.IndexFunc(s, func(r rune) bool { - return !unicode.IsDigit(r) - }) == -1 + return "", fmt.Errorf("process of uid(%d) not found", uid) }