1// +build linux freebsd openbsd darwin solaris 2 3package process 4 5import ( 6 "context" 7 "fmt" 8 "os" 9 "os/user" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "syscall" 14 15 "github.com/shirou/gopsutil/internal/common" 16 "golang.org/x/sys/unix" 17) 18 19// POSIX 20func getTerminalMap() (map[uint64]string, error) { 21 ret := make(map[uint64]string) 22 var termfiles []string 23 24 d, err := os.Open("/dev") 25 if err != nil { 26 return nil, err 27 } 28 defer d.Close() 29 30 devnames, err := d.Readdirnames(-1) 31 if err != nil { 32 return nil, err 33 } 34 for _, devname := range devnames { 35 if strings.HasPrefix(devname, "/dev/tty") { 36 termfiles = append(termfiles, "/dev/tty/"+devname) 37 } 38 } 39 40 var ptsnames []string 41 ptsd, err := os.Open("/dev/pts") 42 if err != nil { 43 ptsnames, _ = filepath.Glob("/dev/ttyp*") 44 if ptsnames == nil { 45 return nil, err 46 } 47 } 48 defer ptsd.Close() 49 50 if ptsnames == nil { 51 defer ptsd.Close() 52 ptsnames, err = ptsd.Readdirnames(-1) 53 if err != nil { 54 return nil, err 55 } 56 for _, ptsname := range ptsnames { 57 termfiles = append(termfiles, "/dev/pts/"+ptsname) 58 } 59 } else { 60 termfiles = ptsnames 61 } 62 63 for _, name := range termfiles { 64 stat := unix.Stat_t{} 65 if err = unix.Stat(name, &stat); err != nil { 66 return nil, err 67 } 68 rdev := uint64(stat.Rdev) 69 ret[rdev] = strings.Replace(name, "/dev", "", -1) 70 } 71 return ret, nil 72} 73 74// isMount is a port of python's os.path.ismount() 75// https://github.com/python/cpython/blob/08ff4369afca84587b1c82034af4e9f64caddbf2/Lib/posixpath.py#L186-L216 76// https://docs.python.org/3/library/os.path.html#os.path.ismount 77func isMount(path string) bool { 78 // Check symlinkness with os.Lstat; unix.DT_LNK is not portable 79 fileInfo, err := os.Lstat(path) 80 if err != nil { 81 return false 82 } 83 if fileInfo.Mode() & os.ModeSymlink != 0 { 84 return false 85 } 86 var stat1 unix.Stat_t 87 if err := unix.Lstat(path, &stat1); err != nil { 88 return false 89 } 90 parent := filepath.Join(path, "..") 91 var stat2 unix.Stat_t 92 if err := unix.Lstat(parent, &stat2); err != nil { 93 return false 94 } 95 return stat1.Dev != stat2.Dev || stat1.Ino == stat2.Ino 96} 97 98func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { 99 if pid <= 0 { 100 return false, fmt.Errorf("invalid pid %v", pid) 101 } 102 proc, err := os.FindProcess(int(pid)) 103 if err != nil { 104 return false, err 105 } 106 107 if isMount(common.HostProc()) { // if /<HOST_PROC>/proc exists and is mounted, check if /<HOST_PROC>/proc/<PID> folder exists 108 _, err := os.Stat(common.HostProc(strconv.Itoa(int(pid)))) 109 if os.IsNotExist(err) { 110 return false, nil 111 } 112 return err == nil, err 113 } 114 115 // procfs does not exist or is not mounted, check PID existence by signalling the pid 116 err = proc.Signal(syscall.Signal(0)) 117 if err == nil { 118 return true, nil 119 } 120 if err.Error() == "os: process already finished" { 121 return false, nil 122 } 123 errno, ok := err.(syscall.Errno) 124 if !ok { 125 return false, err 126 } 127 switch errno { 128 case syscall.ESRCH: 129 return false, nil 130 case syscall.EPERM: 131 return true, nil 132 } 133 134 return false, err 135} 136 137func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error { 138 process, err := os.FindProcess(int(p.Pid)) 139 if err != nil { 140 return err 141 } 142 143 err = process.Signal(sig) 144 if err != nil { 145 return err 146 } 147 148 return nil 149} 150 151func (p *Process) SuspendWithContext(ctx context.Context) error { 152 return p.SendSignalWithContext(ctx, unix.SIGSTOP) 153} 154 155func (p *Process) ResumeWithContext(ctx context.Context) error { 156 return p.SendSignalWithContext(ctx, unix.SIGCONT) 157} 158 159func (p *Process) TerminateWithContext(ctx context.Context) error { 160 return p.SendSignalWithContext(ctx, unix.SIGTERM) 161} 162 163func (p *Process) KillWithContext(ctx context.Context) error { 164 return p.SendSignalWithContext(ctx, unix.SIGKILL) 165} 166 167func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { 168 uids, err := p.UidsWithContext(ctx) 169 if err != nil { 170 return "", err 171 } 172 if len(uids) > 0 { 173 u, err := user.LookupId(strconv.Itoa(int(uids[0]))) 174 if err != nil { 175 return "", err 176 } 177 return u.Username, nil 178 } 179 return "", nil 180} 181