process_openbsd.go 8.5 KB

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