Skip to main content
Version: latest

Go API

This document describes how to use the API provided by Inspektor Gadget to run Gadgets and consume data produced by them from a Golang application.

Concepts

Let's describe some concepts before jumping to the code.

Runtime

A runtime is the component that runs the Gadgets. Inspektor Gadget provides two runtimes:

  • local: Used to run a Gadget on the local host. It's only supported on Linux.
  • grpc: Used to run a Gadget on a remote Linux host. It's supported on Linux, Windows and Mac.

Operators

Operators are the cornerstones of the Inspektor Gadget architecture. They are modular components responsible for specific tasks, such as loading eBPF programs, enriching events, sorting output, etc. Inspektor Gadget provides different operators as described in the operators documentation. It is also possible to create custom operators as explained below.

Data Sources

A Data Source is a mechanism used to transport the data generated by the Gadgets (or operators) across the different components. Operators can generate, consume and mutate data and the layout of the data source.

Gadget Context

The Gadget Context is the component that glues everything together. This carries information between operators, runtimes, etc.

Examples

These examples provide minimal working code to use Gadgets in different ways. Their source code can be found at https://github.com/inspektor-gadget/inspektor-gadget/tree/main/examples/gadgets

Running your first Gadget

Let's start with the minimal code required to run a Gadget. This example shows how to run the trace_open gadget and print the events it captures to the terminal in json format.

// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"fmt"
"os"

// The runtime is the piece that will run our gadget.
"github.com/inspektor-gadget/inspektor-gadget/pkg/runtime/local"

// Import the operators we need:
// - ocihandler: to handle OCI images
// - ebpf: handle ebpf programs
// - simple: building block for our operator
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
_ "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/ebpf"
ocihandler "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/oci-handler"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/simple"

// Datasources provide access to the data produced by gadgets and operators.
// Import also the json formatter to format the data as json.
"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
igjson "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource/formatters/json"

// The gadget context is the glue that connects all components together.
gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
)

func do() error {
// First of all, we need to define a (simple) operator that we'll use to subscribe
// to events. We use a high priority to make sure that our operator is the last
// to run and print the information after all operators have been run.
const opPriority = 50000
myOperator := simple.New("myOperator",
simple.OnInit(func(gadgetCtx operators.GadgetContext) error {
// Subscribe to all datasources and print their output as json the terminal
for _, d := range gadgetCtx.GetDataSources() {
jsonFormatter, _ := igjson.New(d,
// Show all fields
igjson.WithShowAll(true),

// Print json in a pretty format
igjson.WithPretty(true, " "),
)

d.Subscribe(func(source datasource.DataSource, data datasource.Data) error {
jsonOutput := jsonFormatter.Marshal(data)
fmt.Printf("%s\n", jsonOutput)
return nil
}, opPriority)
}
return nil
}),
)

// Then, we create a gadget context instance. This is the glue that connects
// all operators together.
gadgetCtx := gadgetcontext.New(
context.Background(),
// This is the image that contains the gadget we want to run.
"ghcr.io/inspektor-gadget/gadget/trace_open:latest",
// List of operators that will be run with the gadget
gadgetcontext.WithDataOperators(
ocihandler.OciHandler, // pass singleton instance of the oci-handler
myOperator,
),
)

// After that, we need a runtime, that's the piece that will run our gadget.
// In this case, we use the local runtime to run the gadget in the local host.
runtime := local.New()
if err := runtime.Init(nil); err != nil {
return fmt.Errorf("runtime init: %w", err)
}
defer runtime.Close()

// Finally, let's run our gadget
if err := runtime.RunGadget(gadgetCtx, nil, nil); err != nil {
return fmt.Errorf("running gadget: %w", err)
}

return nil
}

func main() {
if err := do(); err != nil {
fmt.Printf("Error running application: %s\n", err)
os.Exit(1)
}
}

Now let's run it. It'll print all the files being opened on the system.

$ go run -exec sudo .
{
"error_raw": 0,
"fd": 7,
"flags_raw": 524288,
"fname": "/sys/fs/cgroup/user.slice/user-1000.slice/[email protected]/memory.pressure",
"mode_raw": 0,
"proc": {
"comm": "systemd-oomd",
"creds": {
"gid": 117,
"uid": 108
},
"mntns_id": 4026532600,
"parent": {
"comm": "systemd",
"pid": 1
},
"pid": 1081,
"tid": 1081
},
"timestamp_raw": 750512507929
}
{
"error_raw": 0,
"fd": 15,
"flags_raw": 0,
"fname": "/sys/class/net/lo/statistics/rx_packets",
"mode_raw": 0,
"proc": {
"comm": "gnome-system-mo",
"creds": {
"gid": 1000,
"uid": 1000
},
"mntns_id": 4026531841,
"parent": {
"comm": "gnome-shell",
"pid": 2601
},
"pid": 16039,
"tid": 16039
},
"timestamp_raw": 750917767023
}
...

Running a Gadget from a file

The example above pulls the trace_open image from the internet if it's not already present on the system. This example shows how to run a Gadget from a file avoiding pulling the image from the internet.

The image export command is used to create a tarball with the trace_open image.

# pull the image if not already present on the system
$ sudo ig image pull trace_open:latest

# export the image to a tarball
$ sudo ig image export trace_open:latest trace_open.tar
// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"fmt"
"os"

orasoci "oras.land/oras-go/v2/content/oci"

"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
igjson "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource/formatters/json"
gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
_ "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/ebpf"
ocihandler "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/oci-handler"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/simple"
"github.com/inspektor-gadget/inspektor-gadget/pkg/runtime/local"
)

func do() error {
ctx := context.Background()

// Create the OCI store from a tarball.
ociStore, err := orasoci.NewFromTar(ctx, "trace_open.tar")
if err != nil {
return fmt.Errorf("getting oci store from tarball: %w", err)
}

const opPriority = 50000
myOperator := simple.New("myOperator",
simple.OnInit(func(gadgetCtx operators.GadgetContext) error {
for _, d := range gadgetCtx.GetDataSources() {
jsonFormatter, _ := igjson.New(d, igjson.WithShowAll(true), igjson.WithPretty(true, " "))
d.Subscribe(func(source datasource.DataSource, data datasource.Data) error {
jsonOutput := jsonFormatter.Marshal(data)
fmt.Printf("%s\n", jsonOutput)
return nil
}, opPriority)
}
return nil
}),
)

gadgetCtx := gadgetcontext.New(
context.Background(),
// The name of the gadget to run is needed as a tarball can contain multiple images.
"ghcr.io/inspektor-gadget/gadget/trace_open:latest",
gadgetcontext.WithDataOperators(ocihandler.OciHandler, myOperator),
gadgetcontext.WithOrasReadonlyTarget(ociStore),
)

runtime := local.New()
if err := runtime.Init(nil); err != nil {
return fmt.Errorf("runtime init: %w", err)
}
defer runtime.Close()

if err := runtime.RunGadget(gadgetCtx, nil, nil); err != nil {
return fmt.Errorf("running gadget: %w", err)
}

return nil
}

func main() {
if err := do(); err != nil {
fmt.Printf("Error running application: %s\n", err)
os.Exit(1)
}
}

Running this example produces the same results as the previous example.

Running a Gadget embedded in your application

The example above required you to ship the tarball file together with your application binary. But you might want to ship the gadget embedded in your binary, so let's explore how that can be done by using //go:embed.

// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"bytes"
"context"
_ "embed"
"fmt"
"os"

"github.com/quay/claircore/pkg/tarfs"
orasoci "oras.land/oras-go/v2/content/oci"

"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
igjson "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource/formatters/json"
gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
_ "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/ebpf"
ocihandler "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/oci-handler"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/simple"
"github.com/inspektor-gadget/inspektor-gadget/pkg/runtime/local"
)

//go:embed trace_open.tar
var traceOpenBytes []byte

func do() error {
ctx := context.Background()

// Create an FS from the tarball bytes
fs, err := tarfs.New(bytes.NewReader(traceOpenBytes))
if err != nil {
return err
}

// Create the oras target from the FS
target, err := orasoci.NewFromFS(ctx, fs)
if err != nil {
return fmt.Errorf("getting oci store from bytes: %w", err)
}

const opPriority = 50000
myOperator := simple.New("myOperator",
simple.OnInit(func(gadgetCtx operators.GadgetContext) error {
for _, d := range gadgetCtx.GetDataSources() {
jsonFormatter, _ := igjson.New(d, igjson.WithShowAll(true), igjson.WithPretty(true, " "))
d.Subscribe(func(source datasource.DataSource, data datasource.Data) error {
jsonOutput := jsonFormatter.Marshal(data)
fmt.Printf("%s\n", jsonOutput)
return nil
}, opPriority)
}
return nil
}),
)

gadgetCtx := gadgetcontext.New(
context.Background(),
// The name of the gadget to run is needed as a tarball can contain multiple images.
"ghcr.io/inspektor-gadget/gadget/trace_open:latest",
gadgetcontext.WithDataOperators(ocihandler.OciHandler, myOperator),
gadgetcontext.WithOrasReadonlyTarget(target),
)

runtime := local.New()
if err := runtime.Init(nil); err != nil {
return fmt.Errorf("runtime init: %w", err)
}
defer runtime.Close()

if err := runtime.RunGadget(gadgetCtx, nil, nil); err != nil {
return fmt.Errorf("running gadget: %w", err)
}

return nil
}

func main() {
if err := do(); err != nil {
fmt.Printf("Error running application: %s\n", err)
os.Exit(1)
}
}

Again, running this will produce the same results as the previous examples.

Using operators from Inspektor Gadget

In this example we use some operators provided by Inspektor Gadget to extend the functionality of your application:

  • LocalManager: Enriches events with container information. Also provides a mechanism to filter events by container.
  • Formatters: Provides a prettier (or human-readable) representation of some fields like timestamps, etc.

Parameters for each operator are passed as a map of strings using the fully qualified name of the parameter as the key.

// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"fmt"
"os"

"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
igjson "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource/formatters/json"
gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
_ "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/ebpf"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/formatters"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/localmanager"
ocihandler "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/oci-handler"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/simple"
"github.com/inspektor-gadget/inspektor-gadget/pkg/runtime/local"
)

func do() error {
ctx := context.Background()

const opPriority = 50000
myOperator := simple.New("myOperator", simple.OnInit(func(gadgetCtx operators.GadgetContext) error {
for _, d := range gadgetCtx.GetDataSources() {
jsonFormatter, _ := igjson.New(d, igjson.WithShowAll(true), igjson.WithPretty(true, " "))
d.Subscribe(func(source datasource.DataSource, data datasource.Data) error {
jsonOutput := jsonFormatter.Marshal(data)
fmt.Printf("%s\n", jsonOutput)
return nil
}, opPriority)
}
return nil
}))

// Configure the local manager operator
localManagerOp := localmanager.LocalManagerOperator
localManagerParams := localManagerOp.GlobalParamDescs().ToParams()
localManagerParams.Get(localmanager.Runtimes).Set("docker")
if err := localManagerOp.Init(localManagerParams); err != nil {
return fmt.Errorf("init local manager: %w", err)
}
defer localManagerOp.Close()

gadgetCtx := gadgetcontext.New(
ctx,
"ghcr.io/inspektor-gadget/gadget/trace_open:latest",
gadgetcontext.WithDataOperators(
ocihandler.OciHandler,
localManagerOp,
formatters.FormattersOperator,
myOperator,
),
)

runtime := local.New()
if err := runtime.Init(nil); err != nil {
return fmt.Errorf("runtime init: %w", err)
}
defer runtime.Close()

params := map[string]string{
// Filter events by container name
"operator.LocalManager.containername": "mycontainer",
}
if err := runtime.RunGadget(gadgetCtx, nil, params); err != nil {
return fmt.Errorf("running gadget: %w", err)
}

return nil
}

func main() {
if err := do(); err != nil {
fmt.Printf("Error running application: %s\n", err)
os.Exit(1)
}
}

Before running the example, let's create a couple of containers that will generate some events:

$ docker run --name mycontainer --rm -d busybox sh -c "while true; do cat /dev/null; sleep 1; done"
$ docker run --name foocontainer --rm -d busybox sh -c "while true; do cat /dev/null; sleep 1; done"

Now, let's run the example.

$ go run -exec sudo .
{
"error": "",
"error_raw": 0,
"fd": 3,
"flags_raw": 0,
"fname": "/dev/null",
"k8s": {
"containerName": "",
"hostnetwork": false,
"namespace": "",
"node": "",
"owner": {
"kind": "",
"name": ""
},
"podLabels": "",
"podName": ""
},
"mode_raw": 0,
"proc": {
"comm": "cat",
"creds": {
"gid": 0,
"uid": 0
},
"mntns_id": 4026534339,
"parent": {
"comm": "sh",
"pid": 42678
},
"pid": 53660,
"tid": 53660
},
"runtime": {
"containerId": "1b0ba0f6f7f7251a8dedbee0a836dc3edadeba7444cd56d6afe0989b6007bcd7",
"containerImageDigest": "sha256:2919d0172f7524b2d8df9e50066a682669e6d170ac0f6a49676d54358fe970b5",
"containerImageName": "busybox",
"containerName": "mycontainer",
"containerPid": 42678,
"containerStartedAt": 1737743296076978537,
"runtimeName": "docker"
},
"timestamp": "2025-01-24T13:37:35.312976962-05:00",
"timestamp_raw": 1737743855312976962
}

As expected, only events from mycontainer are captured and the runtime node contains all information related to the container creating the events. Also, a new timestamp field with a human-readable version is present.

Don't forget to remove the containers created above:

$ docker rm mycontainer foocontainer

Data Source

So far, we've been using the json formatter to convert the events produced by a Gadget to json. However, if you're only interested in getting specific fields, it can be more performant to access them directly using a Field Accessor instead of performing a full json marshaling and unmarshaling.

// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"fmt"
"os"

"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
_ "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/ebpf"
ocihandler "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/oci-handler"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/simple"
"github.com/inspektor-gadget/inspektor-gadget/pkg/runtime/local"
)

func do() error {
const opPriority = 50000
myOperator := simple.New("myOperator",
simple.OnInit(func(gadgetCtx operators.GadgetContext) error {
for _, d := range gadgetCtx.GetDataSources() {
// Access some specific fields
pidF := d.GetField("proc.pid")
commF := d.GetField("proc.comm")
fnameF := d.GetField("fname")

d.Subscribe(func(source datasource.DataSource, data datasource.Data) error {
// Error handling is omitted for brevity
pid, _ := pidF.Uint32(data)
comm, _ := commF.String(data)
fname, _ := fnameF.String(data)
fmt.Printf("command %s (%d) opened %s\n", comm, pid, fname)
return nil
}, opPriority)
}
return nil
}),
)

gadgetCtx := gadgetcontext.New(
context.Background(),
"ghcr.io/inspektor-gadget/gadget/trace_open:latest",
gadgetcontext.WithDataOperators(ocihandler.OciHandler, myOperator),
)

runtime := local.New()
if err := runtime.Init(nil); err != nil {
return fmt.Errorf("runtime init: %w", err)
}
defer runtime.Close()

if err := runtime.RunGadget(gadgetCtx, nil, nil); err != nil {
return fmt.Errorf("running gadget: %w", err)
}

return nil
}

func main() {
if err := do(); err != nil {
fmt.Printf("Error running application: %s\n", err)
os.Exit(1)
}
}

Executing this program will output the following:

$ go run -exec sudo .
command systemd-journal (761) opened /run/log/journal/02962d410aef4be9b1be7d022e8be679/system.journal
command systemd-journal (761) opened /run/log/journal/02962d410aef4be9b1be7d022e8be679/system.journal
command tmux: server (12404) opened /proc/38122/cmdline
command systemd-oomd (1245) opened /proc/meminfo
command systemd-oomd (1245) opened /sys/fs/cgroup/user.slice/user-1001.slice/[email protected]/memory.pressure
command systemd-oomd (1245) opened /sys/fs/cgroup/user.slice/user-1001.slice/[email protected]/memory.current
command systemd-oomd (1245) opened /sys/fs/cgroup/user.slice/user-1001.slice/[email protected]/memory.min
command systemd-oomd (1245) opened /sys/fs/cgroup/user.slice/user-1001.slice/[email protected]/memory.low
command systemd-oomd (1245) opened /sys/fs/cgroup/user.slice/user-1001.slice/[email protected]/memory.swap.current
command systemd-oomd (1245) opened /sys/fs/cgroup/user.slice/user-1001.slice/[email protected]/memory.stat
command systemd-oomd (1245) opened /proc/meminfo
command dbus-daemon (3811) opened /sys/kernel/security/apparmor/.access
command tmux: server (12404) opened /proc/38122/cmdline
command systemd-oomd (1245) opened /proc/meminfo
command systemd-oomd (1245) opened /proc/meminfo
command tmux: server (12404) opened /proc/38122/cmdline

Running Gadgets remotely with the gRPC runtime

All the examples above run the gadgets (and their eBPF parts) locally. This example shows how to control a remote ig instance by using the gRPC runtime. In this case, the Gadget image is pulled by the remote instance, hence the client doesn't need to contact the OCI registry directly.

// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"fmt"
"os"
"os/signal"
"syscall"

"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
igjson "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource/formatters/json"
gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/simple"
grpcruntime "github.com/inspektor-gadget/inspektor-gadget/pkg/runtime/grpc"
)

func do() error {
const opPriority = 50000
myOperator := simple.New("myOperator", simple.OnInit(func(gadgetCtx operators.GadgetContext) error {
for _, d := range gadgetCtx.GetDataSources() {
jsonFormatter, _ := igjson.New(d)

d.Subscribe(func(source datasource.DataSource, data datasource.Data) error {
jsonOutput := jsonFormatter.Marshal(data)
fmt.Printf("%s\n", jsonOutput)
return nil
}, opPriority)
}
return nil
}))

// Used to close the connection in a clean way
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT)
defer stop()

gadgetCtx := gadgetcontext.New(
ctx,
"ghcr.io/inspektor-gadget/gadget/trace_open:latest",
gadgetcontext.WithDataOperators(myOperator),
)

runtime := grpcruntime.New()
runtimeGlobalParams := runtime.GlobalParamDescs().ToParams()
runtimeGlobalParams.Get(grpcruntime.ParamRemoteAddress).Set("tcp://127.0.0.1:8888")
// Unix sockets are also supported:
//runtimeGlobalParams.Get(grpcruntime.ParamRemoteAddress).Set("unix:///var/run/ig/ig.socket")

if err := runtime.Init(runtimeGlobalParams); err != nil {
return fmt.Errorf("runtime init: %w", err)
}
defer runtime.Close()

if err := runtime.RunGadget(gadgetCtx, nil, nil); err != nil {
return fmt.Errorf("running gadget: %w", err)
}

return nil
}

func main() {
if err := do(); err != nil {
fmt.Printf("Error running application: %s\n", err)
os.Exit(1)
}
}

In this case we need to run ig in daemon mode:

$ sudo ig daemon --host tcp://127.0.0.1:8888
INFO[0000] starting Inspektor Gadget daemon at "tcp://127.0.0.1:8888"

Now run the example. Note that in this case running as user "root" is not needed, because all the privileged operations are happening inside the ig process on the remote side.

$ go run .
{"comm":"irqbalance","err":0,"fd":6,"flags":0,"fname":"/proc/interrupts","gid":0,"mntns_id":4026533158,"mode":0,"pid":1262,"timestamp":6114008072805,"uid":0}
{"comm":"irqbalance","err":0,"fd":6,"flags":0,"fname":"/proc/stat","gid":0,"mntns_id":4026533158,"mode":0,"pid":1262,"timestamp":6114008961935,"uid":0}
{"comm":"irqbalance","err":0,"fd":6,"flags":0,"fname":"/proc/irq/65/smp_affinity","gid":0,"mntns_id":4026533158,"mode":0,"pid":1262,"timestamp":6114009113261,"uid":0}
{"comm":"irqbalance","err":0,"fd":6,"flags":0,"fname":"/proc/irq/98/smp_affinity","gid":0,"mntns_id":4026533158,"mode":0,"pid":1262,"timestamp":6114009138809,"uid":0}

Other Examples

There are other examples available in the repo, please check them at https://github.com/inspektor-gadget/inspektor-gadget/tree/main/examples/gadgets/other