From 4c29c2b1c07c17a17f4299e38a84bd4d651a8aa6 Mon Sep 17 00:00:00 2001 From: tink Date: Tue, 23 Jul 2024 22:28:03 +0800 Subject: [PATCH 1/3] fixed setns system call error --- main.go | 6 +++--- pkg/mnt_ns_linux.go | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/main.go b/main.go index 6733d97..17a653c 100644 --- a/main.go +++ b/main.go @@ -32,14 +32,14 @@ import ( "sort" "strings" - "github.com/tobert/pcstat/pkg" + pcstat "github.com/tobert/pcstat/pkg" ) var ( pidFlag int terseFlag, nohdrFlag, jsonFlag, unicodeFlag bool plainFlag, ppsFlag, histoFlag, bnameFlag bool - sortFlag bool + sortFlag bool ) func init() { @@ -61,9 +61,9 @@ func main() { files := flag.Args() if pidFlag != 0 { - pcstat.SwitchMountNs(pidFlag) maps := getPidMaps(pidFlag) files = append(files, maps...) + pcstat.SwitchMountNs(pidFlag) } // all non-flag arguments are considered to be filenames diff --git a/pkg/mnt_ns_linux.go b/pkg/mnt_ns_linux.go index 4832465..c3a864f 100644 --- a/pkg/mnt_ns_linux.go +++ b/pkg/mnt_ns_linux.go @@ -22,6 +22,7 @@ import ( "os" "strconv" "strings" + "syscall" "golang.org/x/sys/unix" ) @@ -36,7 +37,7 @@ func SwitchMountNs(pid int) { pidns := getMountNs(pid) if myns != pidns { - setns(pidns) + setns(pid) } } @@ -63,10 +64,19 @@ func getMountNs(pid int) int { } func setns(fd int) error { - ret, _, err := unix.Syscall(unix.SYS_SETNS, uintptr(uint(fd)), uintptr(CLONE_NEWNS), 0) - if ret != 0 { - return fmt.Errorf("syscall SYS_SETNS failed: %v", err) + if err := unix.Unshare(unix.CLONE_FS); err != nil { + return fmt.Errorf("unshare mount namespace error: %s", err) } + nsMountFileName := fmt.Sprintf("/proc/%d/ns/mnt", fd) + nsMountFile, err := os.Open(nsMountFileName) + if err != nil { + return fmt.Errorf("open mount namespace file error: %s", err) + } + + defer nsMountFile.Close() + if err = unix.Setns(int(nsMountFile.Fd()), syscall.CLONE_NEWNS); err != nil { + return err + } return nil } From 71938c64b05f7d2f72fd779835960e2337e6453c Mon Sep 17 00:00:00 2001 From: tink Date: Wed, 24 Jul 2024 23:16:14 +0800 Subject: [PATCH 2/3] remove unnecessary syscall package --- pkg/mnt_ns_linux.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/mnt_ns_linux.go b/pkg/mnt_ns_linux.go index c3a864f..0cab678 100644 --- a/pkg/mnt_ns_linux.go +++ b/pkg/mnt_ns_linux.go @@ -22,14 +22,10 @@ import ( "os" "strconv" "strings" - "syscall" "golang.org/x/sys/unix" ) -// not available before Go 1.4 -const CLONE_NEWNS = 0x00020000 /* mount namespace */ - // if the pid is in a different mount namespace (e.g. Docker) // the paths will be all wrong, so try to enter that namespace func SwitchMountNs(pid int) { @@ -75,7 +71,7 @@ func setns(fd int) error { } defer nsMountFile.Close() - if err = unix.Setns(int(nsMountFile.Fd()), syscall.CLONE_NEWNS); err != nil { + if err = unix.Setns(int(nsMountFile.Fd()), unix.CLONE_NEWNS); err != nil { return err } return nil From 50baaa6b4db7f89078bf2df4d74fe1147209c576 Mon Sep 17 00:00:00 2001 From: tink Date: Thu, 25 Jul 2024 23:43:46 +0800 Subject: [PATCH 3/3] lock os thread before setns --- pkg/mnt_ns_linux.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/mnt_ns_linux.go b/pkg/mnt_ns_linux.go index 0cab678..c988059 100644 --- a/pkg/mnt_ns_linux.go +++ b/pkg/mnt_ns_linux.go @@ -20,6 +20,7 @@ import ( "fmt" "log" "os" + "runtime" "strconv" "strings" @@ -60,19 +61,26 @@ func getMountNs(pid int) int { } func setns(fd int) error { + // Lock the system thread to prevent the goroutine from + // switching to another system thread after call setnx + runtime.LockOSThread() + + // Go runtime call clone to create a thread, and the flags parameter passed contains CLONE_FS. + // Only by calling unshare to remove CLONE_FS then can setns set CLONE_NEWNS ok. + // See man 2 setnx get more information. if err := unix.Unshare(unix.CLONE_FS); err != nil { - return fmt.Errorf("unshare mount namespace error: %s", err) + return fmt.Errorf("unshare mount namespace error: %w", err) } nsMountFileName := fmt.Sprintf("/proc/%d/ns/mnt", fd) nsMountFile, err := os.Open(nsMountFileName) if err != nil { - return fmt.Errorf("open mount namespace file error: %s", err) + return fmt.Errorf("open mount namespace file error: %w", err) } - defer nsMountFile.Close() + if err = unix.Setns(int(nsMountFile.Fd()), unix.CLONE_NEWNS); err != nil { - return err + return fmt.Errorf("setns mount namespace error: %w", err) } return nil }