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