process_posix.go 4.0 KB

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