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  }