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..c988059 100644 --- a/pkg/mnt_ns_linux.go +++ b/pkg/mnt_ns_linux.go @@ -20,15 +20,13 @@ import ( "fmt" "log" "os" + "runtime" "strconv" "strings" "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) { @@ -36,7 +34,7 @@ func SwitchMountNs(pid int) { pidns := getMountNs(pid) if myns != pidns { - setns(pidns) + setns(pid) } } @@ -63,10 +61,26 @@ 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) + // 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: %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: %w", err) } + defer nsMountFile.Close() + if err = unix.Setns(int(nsMountFile.Fd()), unix.CLONE_NEWNS); err != nil { + return fmt.Errorf("setns mount namespace error: %w", err) + } return nil }