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