github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/signal/tracer/tracer.go (about) 1 // Copyright 2019-2023 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 "strconv" 24 "strings" 25 "syscall" 26 "unsafe" 27 28 "github.com/cilium/ebpf" 29 "github.com/cilium/ebpf/link" 30 "github.com/cilium/ebpf/perf" 31 32 gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" 33 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/signal/types" 35 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 36 37 "golang.org/x/sys/unix" 38 ) 39 40 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type event sigsnoop ./bpf/sigsnoop.bpf.c -- 41 42 type Config struct { 43 MountnsMap *ebpf.Map 44 TargetSignal string 45 TargetPid int32 46 FailedOnly bool 47 KillOnly bool 48 } 49 50 type Tracer struct { 51 config *Config 52 53 objs sigsnoopObjects 54 enterKillLink link.Link 55 exitKillLink link.Link 56 enterTkillLink link.Link 57 exitTkillLink link.Link 58 enterTgkillLink link.Link 59 exitTgkillLink link.Link 60 signalGenerateLink link.Link 61 reader *perf.Reader 62 63 enricher gadgets.DataEnricherByMntNs 64 eventCallback func(*types.Event) 65 } 66 67 func signalIntToString(signal int) string { 68 return unix.SignalName(syscall.Signal(signal)) 69 } 70 71 func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs, 72 eventCallback func(*types.Event), 73 ) (*Tracer, error) { 74 t := &Tracer{ 75 config: config, 76 enricher: enricher, 77 eventCallback: eventCallback, 78 } 79 80 if err := t.install(); err != nil { 81 t.close() 82 return nil, err 83 } 84 85 go t.run() 86 87 return t, nil 88 } 89 90 // Stop stops the tracer 91 // TODO: Remove after refactoring 92 func (t *Tracer) Stop() { 93 t.close() 94 } 95 96 func (t *Tracer) close() { 97 t.enterKillLink = gadgets.CloseLink(t.enterKillLink) 98 t.exitKillLink = gadgets.CloseLink(t.exitKillLink) 99 100 t.enterTkillLink = gadgets.CloseLink(t.enterTkillLink) 101 t.exitTkillLink = gadgets.CloseLink(t.exitTkillLink) 102 103 t.enterTgkillLink = gadgets.CloseLink(t.enterTgkillLink) 104 t.exitTgkillLink = gadgets.CloseLink(t.exitTgkillLink) 105 106 t.signalGenerateLink = gadgets.CloseLink(t.signalGenerateLink) 107 108 if t.reader != nil { 109 t.reader.Close() 110 } 111 112 t.objs.Close() 113 } 114 115 func (t *Tracer) install() error { 116 spec, err := loadSigsnoop() 117 if err != nil { 118 return fmt.Errorf("loading ebpf program: %w", err) 119 } 120 121 signal, err := signalStringToInt(t.config.TargetSignal) 122 if err != nil { 123 return fmt.Errorf("converting signal (%q) to int: %w", t.config.TargetSignal, err) 124 } 125 126 consts := map[string]interface{}{ 127 "filtered_pid": t.config.TargetPid, 128 "target_signal": signal, 129 "failed_only": t.config.FailedOnly, 130 } 131 132 if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil { 133 return fmt.Errorf("loading ebpf spec: %w", err) 134 } 135 136 if t.config.KillOnly { 137 t.enterKillLink, err = link.Tracepoint("syscalls", "sys_enter_kill", t.objs.IgSigKillE, nil) 138 if err != nil { 139 return fmt.Errorf("attaching tracepoint sys_enter_kill: %w", err) 140 } 141 142 t.exitKillLink, err = link.Tracepoint("syscalls", "sys_exit_kill", t.objs.IgSigKillX, nil) 143 if err != nil { 144 return fmt.Errorf("attaching tracepoint sys_exit_kill: %w", err) 145 } 146 147 t.enterTkillLink, err = link.Tracepoint("syscalls", "sys_enter_tkill", t.objs.IgSigTkillE, nil) 148 if err != nil { 149 return fmt.Errorf("attaching tracepoint sys_enter_tkill: %w", err) 150 } 151 152 t.exitTkillLink, err = link.Tracepoint("syscalls", "sys_exit_tkill", t.objs.IgSigTkillX, nil) 153 if err != nil { 154 return fmt.Errorf("attaching tracepoint sys_exit_tkill: %w", err) 155 } 156 157 t.enterTgkillLink, err = link.Tracepoint("syscalls", "sys_enter_tgkill", t.objs.IgSigTgkillE, nil) 158 if err != nil { 159 return fmt.Errorf("attaching tracepoint sys_enter_tgkill: %w", err) 160 } 161 162 t.exitTgkillLink, err = link.Tracepoint("syscalls", "sys_exit_tgkill", t.objs.IgSigTgkillX, nil) 163 if err != nil { 164 return fmt.Errorf("attaching tracepoint sys_exit_tgkill: %w", err) 165 } 166 } else { 167 t.signalGenerateLink, err = link.Tracepoint("signal", "signal_generate", t.objs.IgSigGenerate, nil) 168 if err != nil { 169 return fmt.Errorf("attaching tracepoint signal_generate: %w", err) 170 } 171 } 172 173 t.reader, err = perf.NewReader(t.objs.sigsnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 174 if err != nil { 175 return fmt.Errorf("creating perf ring buffer: %w", err) 176 } 177 178 return nil 179 } 180 181 func (t *Tracer) run() { 182 for { 183 record, err := t.reader.Read() 184 if err != nil { 185 if errors.Is(err, perf.ErrClosed) { 186 // nothing to do, we're done 187 return 188 } 189 190 msg := fmt.Sprintf("Error reading perf ring buffer: %s", err) 191 t.eventCallback(types.Base(eventtypes.Err(msg))) 192 return 193 } 194 195 if record.LostSamples > 0 { 196 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 197 t.eventCallback(types.Base(eventtypes.Warn(msg))) 198 continue 199 } 200 201 bpfEvent := (*sigsnoopEvent)(unsafe.Pointer(&record.RawSample[0])) 202 203 event := types.Event{ 204 Event: eventtypes.Event{ 205 Type: eventtypes.NORMAL, 206 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 207 }, 208 Pid: bpfEvent.Pid, 209 TargetPid: bpfEvent.Tpid, 210 Signal: signalIntToString(int(bpfEvent.Sig)), 211 Retval: int(bpfEvent.Ret), 212 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId}, 213 Comm: gadgets.FromCString(bpfEvent.Comm[:]), 214 Uid: bpfEvent.Uid, 215 Gid: bpfEvent.Gid, 216 } 217 218 if t.enricher != nil { 219 t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID) 220 } 221 222 t.eventCallback(&event) 223 } 224 } 225 226 // --- Registry changes 227 228 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 229 params := gadgetCtx.GadgetParams() 230 t.config.TargetPid = params.Get(ParamPID).AsInt32() 231 t.config.FailedOnly = params.Get(ParamFailedOnly).AsBool() 232 t.config.KillOnly = params.Get(ParamKillOnly).AsBool() 233 t.config.TargetSignal = params.Get(ParamTargetSignal).AsString() 234 235 defer t.close() 236 if err := t.install(); err != nil { 237 return fmt.Errorf("installing tracer: %w", err) 238 } 239 240 go t.run() 241 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 242 243 return nil 244 } 245 246 func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) { 247 t.config.MountnsMap = mountnsMap 248 } 249 250 func (t *Tracer) SetEventHandler(handler any) { 251 nh, ok := handler.(func(ev *types.Event)) 252 if !ok { 253 panic("event handler invalid") 254 } 255 t.eventCallback = nh 256 } 257 258 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 259 tracer := &Tracer{ 260 config: &Config{}, 261 } 262 return tracer, nil 263 } 264 265 func signalStringToInt(signal string) (int32, error) { 266 // There are three possibilities: 267 // 1. Either user did not give a signal, thus the argument is empty string. 268 // 2. Or signal begins with SIG. 269 // 3. Or signal is a string which contains an integer. 270 if signal == "" { 271 return 0, nil 272 } 273 274 if strings.HasPrefix(signal, "SIG") { 275 signalNum := unix.SignalNum(signal) 276 if signalNum == 0 { 277 return 0, fmt.Errorf("no signal found for %q", signal) 278 } 279 280 return int32(signalNum), nil 281 } 282 283 signalNum, err := strconv.ParseInt(signal, 10, 32) 284 285 return int32(signalNum), err 286 }