github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/exec/tracer/tracer.go (about) 1 // Copyright 2019-2024 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build !withoutebpf 16 17 package tracer 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "unsafe" 24 25 "github.com/cilium/ebpf" 26 "github.com/cilium/ebpf/link" 27 "github.com/cilium/ebpf/perf" 28 29 gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" 30 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types" 32 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 33 ) 34 35 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type event execsnoop ./bpf/execsnoop.bpf.c -- -I./bpf/ 36 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type event execsnoopWithLongPaths ./bpf/execsnoop.bpf.c -- -DWITH_LONG_PATHS -I./bpf/ 37 38 // needs to be kept in sync with execsnoopEvent from execsnoop_bpfel.go without the Args field 39 type execsnoopEventAbbrev struct { 40 MntnsId uint64 41 Timestamp uint64 42 Pid uint32 43 Ppid uint32 44 Uid uint32 45 Gid uint32 46 Loginuid uint32 47 Sessionid uint32 48 Retval int32 49 ArgsCount int32 50 UpperLayer bool 51 _ [3]byte 52 ArgsSize uint32 53 Comm [16]uint8 54 Pcomm [16]uint8 55 } 56 57 // needs to be kept in sync with execsnoopWithLongPathsEvent from execsnoopwithlongpaths_bpfel.go without the Args field 58 type execsnoopWithLongPathsEventAbbrev struct { 59 MntnsId uint64 60 Timestamp uint64 61 Pid uint32 62 Ppid uint32 63 Uid uint32 64 Gid uint32 65 Loginuid uint32 66 Sessionid uint32 67 Retval int32 68 ArgsCount int32 69 UpperLayer bool 70 _ [3]byte 71 ArgsSize uint32 72 Comm [16]uint8 73 Pcomm [16]uint8 74 Cwd [4096]uint8 75 ExePath [4096]uint8 76 } 77 78 type Config struct { 79 MountnsMap *ebpf.Map 80 GetPaths bool 81 IgnoreErrors bool 82 } 83 84 type Tracer struct { 85 config *Config 86 enricher gadgets.DataEnricherByMntNs 87 eventCallback func(*types.Event) 88 89 objs execsnoopObjects 90 enterLink link.Link 91 exitLink link.Link 92 reader *perf.Reader 93 } 94 95 func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs, 96 eventCallback func(*types.Event), 97 ) (*Tracer, error) { 98 t := &Tracer{ 99 config: config, 100 enricher: enricher, 101 eventCallback: eventCallback, 102 } 103 104 if err := t.install(); err != nil { 105 t.close() 106 return nil, err 107 } 108 109 go t.run() 110 111 return t, nil 112 } 113 114 // Stop stops the tracer 115 // TODO: Remove after refactoring 116 func (t *Tracer) Stop() { 117 t.close() 118 } 119 120 func (t *Tracer) close() { 121 t.enterLink = gadgets.CloseLink(t.enterLink) 122 t.exitLink = gadgets.CloseLink(t.exitLink) 123 124 if t.reader != nil { 125 t.reader.Close() 126 } 127 128 t.objs.Close() 129 } 130 131 func (t *Tracer) install() error { 132 var spec *ebpf.CollectionSpec 133 var err error 134 135 if t.config.GetPaths { 136 spec, err = loadExecsnoopWithLongPaths() 137 } else { 138 spec, err = loadExecsnoop() 139 } 140 if err != nil { 141 return fmt.Errorf("loading ebpf program: %w", err) 142 } 143 144 consts := map[string]interface{}{ 145 "ignore_failed": t.config.IgnoreErrors, 146 } 147 148 if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil { 149 return fmt.Errorf("loading ebpf spec: %w", err) 150 } 151 152 t.enterLink, err = link.Tracepoint("syscalls", "sys_enter_execve", t.objs.IgExecveE, nil) 153 if err != nil { 154 return fmt.Errorf("attaching enter tracepoint: %w", err) 155 } 156 157 t.exitLink, err = link.Tracepoint("syscalls", "sys_exit_execve", t.objs.IgExecveX, nil) 158 if err != nil { 159 return fmt.Errorf("attaching exit tracepoint: %w", err) 160 } 161 162 reader, err := perf.NewReader(t.objs.execsnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 163 if err != nil { 164 return fmt.Errorf("creating perf ring buffer: %w", err) 165 } 166 t.reader = reader 167 168 return nil 169 } 170 171 func (t *Tracer) run() { 172 for { 173 record, err := t.reader.Read() 174 if err != nil { 175 if errors.Is(err, perf.ErrClosed) { 176 // nothing to do, we're done 177 return 178 } 179 180 msg := fmt.Sprintf("Error reading perf ring buffer: %s", err) 181 t.eventCallback(types.Base(eventtypes.Err(msg))) 182 return 183 } 184 185 if record.LostSamples > 0 { 186 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 187 t.eventCallback(types.Base(eventtypes.Warn(msg))) 188 continue 189 } 190 191 // this works regardless the kind of event because cwd is defined at the end of the 192 // structure. (Just before args that are handled in a different way below) 193 bpfEvent := (*execsnoopEventAbbrev)(unsafe.Pointer(&record.RawSample[0])) 194 195 event := types.Event{ 196 Event: eventtypes.Event{ 197 Type: eventtypes.NORMAL, 198 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 199 }, 200 Pid: bpfEvent.Pid, 201 Ppid: bpfEvent.Ppid, 202 Uid: bpfEvent.Uid, 203 Gid: bpfEvent.Gid, 204 LoginUid: bpfEvent.Loginuid, 205 SessionId: bpfEvent.Sessionid, 206 UpperLayer: bpfEvent.UpperLayer, 207 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId}, 208 Retval: int(bpfEvent.Retval), 209 Comm: gadgets.FromCString(bpfEvent.Comm[:]), 210 Pcomm: gadgets.FromCString(bpfEvent.Pcomm[:]), 211 } 212 213 argsCount := 0 214 buf := []byte{} 215 args := record.RawSample[unsafe.Offsetof(execsnoopEvent{}.Args):] 216 217 if t.config.GetPaths { 218 bpfEventWithLongPaths := (*execsnoopWithLongPathsEventAbbrev)(unsafe.Pointer(&record.RawSample[0])) 219 event.Cwd = gadgets.FromCString(bpfEventWithLongPaths.Cwd[:]) 220 event.ExePath = gadgets.FromCString(bpfEventWithLongPaths.ExePath[:]) 221 args = record.RawSample[unsafe.Offsetof(execsnoopWithLongPathsEvent{}.Args):] 222 } 223 224 for i := 0; i < int(bpfEvent.ArgsSize) && argsCount < int(bpfEvent.ArgsCount); i++ { 225 c := args[i] 226 if c == 0 { 227 event.Args = append(event.Args, string(buf)) 228 argsCount = 0 229 buf = []byte{} 230 } else { 231 buf = append(buf, c) 232 } 233 } 234 235 if t.enricher != nil { 236 t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID) 237 } 238 239 t.eventCallback(&event) 240 } 241 } 242 243 // --- Registry changes 244 245 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 246 t.config.GetPaths = gadgetCtx.GadgetParams().Get(ParamPaths).AsBool() 247 t.config.IgnoreErrors = gadgetCtx.GadgetParams().Get(ParamIgnoreErrors).AsBool() 248 249 defer t.close() 250 if err := t.install(); err != nil { 251 return fmt.Errorf("installing tracer: %w", err) 252 } 253 254 go t.run() 255 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 256 257 return nil 258 } 259 260 func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) { 261 t.config.MountnsMap = mountnsMap 262 } 263 264 func (t *Tracer) SetEventHandler(handler any) { 265 nh, ok := handler.(func(ev *types.Event)) 266 if !ok { 267 panic("event handler invalid") 268 } 269 t.eventCallback = nh 270 } 271 272 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 273 tracer := &Tracer{ 274 config: &Config{}, 275 } 276 return tracer, nil 277 }