process_openbsd.go 8.4 KB

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