Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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
Expand Down
28 changes: 21 additions & 7 deletions pkg/mnt_ns_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,21 @@ 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) {
myns := getMountNs(os.Getpid())
pidns := getMountNs(pid)

if myns != pidns {
setns(pidns)
setns(pid)
}
}

Expand All @@ -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
}