1// +build openbsd 2 3package process 4 5import ( 6 "bytes" 7 "context" 8 "encoding/binary" 9 "fmt" 10 "io" 11 "os/exec" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "unsafe" 16 17 cpu "github.com/shirou/gopsutil/v3/cpu" 18 "github.com/shirou/gopsutil/v3/internal/common" 19 mem "github.com/shirou/gopsutil/v3/mem" 20 net "github.com/shirou/gopsutil/v3/net" 21 "golang.org/x/sys/unix" 22) 23 24func pidsWithContext(ctx context.Context) ([]int32, error) { 25 var ret []int32 26 procs, err := ProcessesWithContext(ctx) 27 if err != nil { 28 return ret, nil 29 } 30 31 for _, p := range procs { 32 ret = append(ret, p.Pid) 33 } 34 35 return ret, nil 36} 37 38func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { 39 k, err := p.getKProc() 40 if err != nil { 41 return 0, err 42 } 43 44 return k.Ppid, nil 45} 46 47func (p *Process) NameWithContext(ctx context.Context) (string, error) { 48 k, err := p.getKProc() 49 if err != nil { 50 return "", err 51 } 52 name := common.IntToString(k.Comm[:]) 53 54 if len(name) >= 15 { 55 cmdlineSlice, err := p.CmdlineSliceWithContext(ctx) 56 if err != nil { 57 return "", err 58 } 59 if len(cmdlineSlice) > 0 { 60 extendedName := filepath.Base(cmdlineSlice[0]) 61 if strings.HasPrefix(extendedName, p.name) { 62 name = extendedName 63 } else { 64 name = cmdlineSlice[0] 65 } 66 } 67 } 68 69 return name, nil 70} 71 72func (p *Process) ExeWithContext(ctx context.Context) (string, error) { 73 return "", common.ErrNotImplementedError 74} 75 76func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { 77 mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv} 78 buf, _, err := common.CallSyscall(mib) 79 80 if err != nil { 81 return nil, err 82 } 83 84 /* From man sysctl(2): 85 The buffer pointed to by oldp is filled with an array of char 86 pointers followed by the strings themselves. The last char 87 pointer is a NULL pointer. */ 88 var strParts []string 89 r := bytes.NewReader(buf) 90 baseAddr := uintptr(unsafe.Pointer(&buf[0])) 91 for { 92 argvp, err := readPtr(r) 93 if err != nil { 94 return nil, err 95 } 96 if argvp == 0 { // check for a NULL pointer 97 break 98 } 99 offset := argvp - baseAddr 100 length := uintptr(bytes.IndexByte(buf[offset:], 0)) 101 str := string(buf[offset : offset+length]) 102 strParts = append(strParts, str) 103 } 104 105 return strParts, nil 106} 107 108// readPtr reads a pointer data from a given reader. WARNING: only little 109// endian architectures are supported. 110func readPtr(r io.Reader) (uintptr, error) { 111 switch sizeofPtr { 112 case 4: 113 var p uint32 114 if err := binary.Read(r, binary.LittleEndian, &p); err != nil { 115 return 0, err 116 } 117 return uintptr(p), nil 118 case 8: 119 var p uint64 120 if err := binary.Read(r, binary.LittleEndian, &p); err != nil { 121 return 0, err 122 } 123 return uintptr(p), nil 124 default: 125 return 0, fmt.Errorf("unsupported pointer size") 126 } 127} 128 129func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { 130 argv, err := p.CmdlineSliceWithContext(ctx) 131 if err != nil { 132 return "", err 133 } 134 return strings.Join(argv, " "), nil 135} 136 137func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { 138 return 0, common.ErrNotImplementedError 139} 140 141func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { 142 return nil, common.ErrNotImplementedError 143} 144 145func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { 146 k, err := p.getKProc() 147 if err != nil { 148 return []string{""}, err 149 } 150 var s string 151 switch k.Stat { 152 case SIDL: 153 case SRUN: 154 case SONPROC: 155 s = Running 156 case SSLEEP: 157 s = Sleep 158 case SSTOP: 159 s = Stop 160 case SDEAD: 161 s = Zombie 162 } 163 164 return []string{s}, nil 165} 166 167func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { 168 // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details 169 pid := p.Pid 170 ps, err := exec.LookPath("ps") 171 if err != nil { 172 return false, err 173 } 174 out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid))) 175 if err != nil { 176 return false, err 177 } 178 return strings.IndexByte(string(out), '+') != -1, nil 179} 180 181func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { 182 k, err := p.getKProc() 183 if err != nil { 184 return nil, err 185 } 186 187 uids := make([]int32, 0, 3) 188 189 uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid)) 190 191 return uids, nil 192} 193 194func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { 195 k, err := p.getKProc() 196 if err != nil { 197 return nil, err 198 } 199 200 gids := make([]int32, 0, 3) 201 gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid)) 202 203 return gids, nil 204} 205 206func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { 207 k, err := p.getKProc() 208 if err != nil { 209 return nil, err 210 } 211 212 groups := make([]int32, k.Ngroups) 213 for i := int16(0); i < k.Ngroups; i++ { 214 groups[i] = int32(k.Groups[i]) 215 } 216 217 return groups, nil 218} 219 220func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { 221 k, err := p.getKProc() 222 if err != nil { 223 return "", err 224 } 225 226 ttyNr := uint64(k.Tdev) 227 228 termmap, err := getTerminalMap() 229 if err != nil { 230 return "", err 231 } 232 233 return termmap[ttyNr], nil 234} 235 236func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { 237 k, err := p.getKProc() 238 if err != nil { 239 return 0, err 240 } 241 return int32(k.Nice), nil 242} 243 244func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { 245 k, err := p.getKProc() 246 if err != nil { 247 return nil, err 248 } 249 return &IOCountersStat{ 250 ReadCount: uint64(k.Uru_inblock), 251 WriteCount: uint64(k.Uru_oublock), 252 }, nil 253} 254 255func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { 256 /* not supported, just return 1 */ 257 return 1, nil 258} 259 260func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { 261 k, err := p.getKProc() 262 if err != nil { 263 return nil, err 264 } 265 return &cpu.TimesStat{ 266 CPU: "cpu", 267 User: float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000, 268 System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000, 269 }, nil 270} 271 272func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { 273 k, err := p.getKProc() 274 if err != nil { 275 return nil, err 276 } 277 pageSize, err := mem.GetPageSizeWithContext(ctx) 278 if err != nil { 279 return nil, err 280 } 281 282 return &MemoryInfoStat{ 283 RSS: uint64(k.Vm_rssize) * pageSize, 284 VMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) + 285 uint64(k.Vm_ssize), 286 }, nil 287} 288 289func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { 290 pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) 291 if err != nil { 292 return nil, err 293 } 294 ret := make([]*Process, 0, len(pids)) 295 for _, pid := range pids { 296 np, err := NewProcessWithContext(ctx, pid) 297 if err != nil { 298 return nil, err 299 } 300 ret = append(ret, np) 301 } 302 return ret, nil 303} 304 305func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { 306 return nil, common.ErrNotImplementedError 307} 308 309func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { 310 return nil, common.ErrNotImplementedError 311} 312 313func ProcessesWithContext(ctx context.Context) ([]*Process, error) { 314 results := []*Process{} 315 316 buf, length, err := callKernProcSyscall(KernProcAll, 0) 317 318 if err != nil { 319 return results, err 320 } 321 322 // get kinfo_proc size 323 count := int(length / uint64(sizeOfKinfoProc)) 324 325 // parse buf to procs 326 for i := 0; i < count; i++ { 327 b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc] 328 k, err := parseKinfoProc(b) 329 if err != nil { 330 continue 331 } 332 p, err := NewProcessWithContext(ctx, int32(k.Pid)) 333 if err != nil { 334 continue 335 } 336 337 results = append(results, p) 338 } 339 340 return results, nil 341} 342 343func (p *Process) getKProc() (*KinfoProc, error) { 344 buf, length, err := callKernProcSyscall(KernProcPID, p.Pid) 345 if err != nil { 346 return nil, err 347 } 348 if length != sizeOfKinfoProc { 349 return nil, err 350 } 351 352 k, err := parseKinfoProc(buf) 353 if err != nil { 354 return nil, err 355 } 356 return &k, nil 357} 358 359func callKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) { 360 mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0} 361 mibptr := unsafe.Pointer(&mib[0]) 362 miblen := uint64(len(mib)) 363 length := uint64(0) 364 _, _, err := unix.Syscall6( 365 unix.SYS___SYSCTL, 366 uintptr(mibptr), 367 uintptr(miblen), 368 0, 369 uintptr(unsafe.Pointer(&length)), 370 0, 371 0) 372 if err != 0 { 373 return nil, length, err 374 } 375 376 count := int32(length / uint64(sizeOfKinfoProc)) 377 mib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count} 378 mibptr = unsafe.Pointer(&mib[0]) 379 miblen = uint64(len(mib)) 380 // get proc info itself 381 buf := make([]byte, length) 382 _, _, err = unix.Syscall6( 383 unix.SYS___SYSCTL, 384 uintptr(mibptr), 385 uintptr(miblen), 386 uintptr(unsafe.Pointer(&buf[0])), 387 uintptr(unsafe.Pointer(&length)), 388 0, 389 0) 390 if err != 0 { 391 return buf, length, err 392 } 393 394 return buf, length, nil 395} 396