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