psnotify_linux.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright (c) 2012 VMware, Inc.
  2. // Go interface to the Linux netlink process connector.
  3. // See Documentation/connector/connector.txt in the linux kernel source tree.
  4. package psnotify
  5. import (
  6. "bytes"
  7. "encoding/binary"
  8. "os"
  9. "syscall"
  10. "notabug.org/themusicgod1/gosigar/sys"
  11. )
  12. const (
  13. // internal flags (from <linux/connector.h>)
  14. _CN_IDX_PROC = 0x1
  15. _CN_VAL_PROC = 0x1
  16. // internal flags (from <linux/cn_proc.h>)
  17. _PROC_CN_MCAST_LISTEN = 1
  18. _PROC_CN_MCAST_IGNORE = 2
  19. // Flags (from <linux/cn_proc.h>)
  20. PROC_EVENT_FORK = 0x00000001 // fork() events
  21. PROC_EVENT_EXEC = 0x00000002 // exec() events
  22. PROC_EVENT_EXIT = 0x80000000 // exit() events
  23. // Watch for all process events
  24. PROC_EVENT_ALL = PROC_EVENT_FORK | PROC_EVENT_EXEC | PROC_EVENT_EXIT
  25. )
  26. var (
  27. byteOrder = sys.GetEndian()
  28. )
  29. // linux/connector.h: struct cb_id
  30. type cbId struct {
  31. Idx uint32
  32. Val uint32
  33. }
  34. // linux/connector.h: struct cb_msg
  35. type cnMsg struct {
  36. Id cbId
  37. Seq uint32
  38. Ack uint32
  39. Len uint16
  40. Flags uint16
  41. }
  42. // linux/cn_proc.h: struct proc_event.{what,cpu,timestamp_ns}
  43. type procEventHeader struct {
  44. What uint32
  45. Cpu uint32
  46. Timestamp uint64
  47. }
  48. // linux/cn_proc.h: struct proc_event.fork
  49. type forkProcEvent struct {
  50. ParentPid uint32
  51. ParentTgid uint32
  52. ChildPid uint32
  53. ChildTgid uint32
  54. }
  55. // linux/cn_proc.h: struct proc_event.exec
  56. type execProcEvent struct {
  57. ProcessPid uint32
  58. ProcessTgid uint32
  59. }
  60. // linux/cn_proc.h: struct proc_event.exit
  61. type exitProcEvent struct {
  62. ProcessPid uint32
  63. ProcessTgid uint32
  64. ExitCode uint32
  65. ExitSignal uint32
  66. }
  67. // standard netlink header + connector header
  68. type netlinkProcMessage struct {
  69. Header syscall.NlMsghdr
  70. Data cnMsg
  71. }
  72. type netlinkListener struct {
  73. addr *syscall.SockaddrNetlink // Netlink socket address
  74. sock int // The syscall.Socket() file descriptor
  75. seq uint32 // struct cn_msg.seq
  76. }
  77. // Initialize linux implementation of the eventListener interface
  78. func createListener() (eventListener, error) {
  79. listener := &netlinkListener{}
  80. err := listener.bind()
  81. return listener, err
  82. }
  83. // noop on linux
  84. func (w *Watcher) unregister(pid int) error {
  85. return nil
  86. }
  87. // noop on linux
  88. func (w *Watcher) register(pid int, flags uint32) error {
  89. return nil
  90. }
  91. // Read events from the netlink socket
  92. func (w *Watcher) readEvents() {
  93. buf := make([]byte, syscall.Getpagesize())
  94. listener, _ := w.listener.(*netlinkListener)
  95. for {
  96. if w.isDone() {
  97. return
  98. }
  99. nr, _, err := syscall.Recvfrom(listener.sock, buf, 0)
  100. if err != nil {
  101. w.Error <- err
  102. continue
  103. }
  104. if nr < syscall.NLMSG_HDRLEN {
  105. w.Error <- syscall.EINVAL
  106. continue
  107. }
  108. msgs, _ := syscall.ParseNetlinkMessage(buf[:nr])
  109. for _, m := range msgs {
  110. if m.Header.Type == syscall.NLMSG_DONE {
  111. w.handleEvent(m.Data)
  112. }
  113. }
  114. }
  115. }
  116. // Internal helper to check if pid && event is being watched
  117. func (w *Watcher) isWatching(pid int, event uint32) bool {
  118. if watch, ok := w.watches[pid]; ok {
  119. return (watch.flags & event) == event
  120. }
  121. return false
  122. }
  123. // Dispatch events from the netlink socket to the Event channels.
  124. // Unlike bsd kqueue, netlink receives events for all pids,
  125. // so we apply filtering based on the watch table via isWatching()
  126. func (w *Watcher) handleEvent(data []byte) {
  127. buf := bytes.NewBuffer(data)
  128. msg := &cnMsg{}
  129. hdr := &procEventHeader{}
  130. binary.Read(buf, byteOrder, msg)
  131. binary.Read(buf, byteOrder, hdr)
  132. switch hdr.What {
  133. case PROC_EVENT_FORK:
  134. event := &forkProcEvent{}
  135. binary.Read(buf, byteOrder, event)
  136. ppid := int(event.ParentTgid)
  137. pid := int(event.ChildTgid)
  138. if w.isWatching(ppid, PROC_EVENT_EXEC) {
  139. // follow forks
  140. watch, _ := w.watches[ppid]
  141. w.Watch(pid, watch.flags)
  142. }
  143. if w.isWatching(ppid, PROC_EVENT_FORK) {
  144. w.Fork <- &ProcEventFork{ParentPid: ppid, ChildPid: pid}
  145. }
  146. case PROC_EVENT_EXEC:
  147. event := &execProcEvent{}
  148. binary.Read(buf, byteOrder, event)
  149. pid := int(event.ProcessTgid)
  150. if w.isWatching(pid, PROC_EVENT_EXEC) {
  151. w.Exec <- &ProcEventExec{Pid: pid}
  152. }
  153. case PROC_EVENT_EXIT:
  154. event := &exitProcEvent{}
  155. binary.Read(buf, byteOrder, event)
  156. pid := int(event.ProcessTgid)
  157. if w.isWatching(pid, PROC_EVENT_EXIT) {
  158. w.RemoveWatch(pid)
  159. w.Exit <- &ProcEventExit{Pid: pid}
  160. }
  161. }
  162. }
  163. // Bind our netlink socket and
  164. // send a listen control message to the connector driver.
  165. func (listener *netlinkListener) bind() error {
  166. sock, err := syscall.Socket(
  167. syscall.AF_NETLINK,
  168. syscall.SOCK_DGRAM,
  169. syscall.NETLINK_CONNECTOR)
  170. if err != nil {
  171. return err
  172. }
  173. listener.sock = sock
  174. listener.addr = &syscall.SockaddrNetlink{
  175. Family: syscall.AF_NETLINK,
  176. Groups: _CN_IDX_PROC,
  177. }
  178. err = syscall.Bind(listener.sock, listener.addr)
  179. if err != nil {
  180. return err
  181. }
  182. return listener.send(_PROC_CN_MCAST_LISTEN)
  183. }
  184. // Send an ignore control message to the connector driver
  185. // and close our netlink socket.
  186. func (listener *netlinkListener) close() error {
  187. err := listener.send(_PROC_CN_MCAST_IGNORE)
  188. syscall.Close(listener.sock)
  189. return err
  190. }
  191. // Generic method for sending control messages to the connector
  192. // driver; where op is one of PROC_CN_MCAST_{LISTEN,IGNORE}
  193. func (listener *netlinkListener) send(op uint32) error {
  194. listener.seq++
  195. pr := &netlinkProcMessage{}
  196. plen := binary.Size(pr.Data) + binary.Size(op)
  197. pr.Header.Len = syscall.NLMSG_HDRLEN + uint32(plen)
  198. pr.Header.Type = uint16(syscall.NLMSG_DONE)
  199. pr.Header.Flags = 0
  200. pr.Header.Seq = listener.seq
  201. pr.Header.Pid = uint32(os.Getpid())
  202. pr.Data.Id.Idx = _CN_IDX_PROC
  203. pr.Data.Id.Val = _CN_VAL_PROC
  204. pr.Data.Len = uint16(binary.Size(op))
  205. buf := bytes.NewBuffer(make([]byte, 0, pr.Header.Len))
  206. binary.Write(buf, byteOrder, pr)
  207. binary.Write(buf, byteOrder, op)
  208. return syscall.Sendto(listener.sock, buf.Bytes(), 0, listener.addr)
  209. }