Skip to content

Commit

Permalink
chore(cmd,pkg): support automatic kernel headers download/extraction …
Browse files Browse the repository at this point in the history
…in local builder.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
  • Loading branch information
FedeDP authored and poiana committed Mar 25, 2024
1 parent 4f6ee10 commit cd9e5b3
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 45 deletions.
45 changes: 13 additions & 32 deletions cmd/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,33 @@ import (
"github.com/spf13/viper"
"log/slog"
"os"
"os/user"
"runtime"
)

type localCmdOptions struct {
useDKMS bool
srcDir string
envMap map[string]string
useDKMS bool
downloadHeaders bool
srcDir string
envMap map[string]string
}

// NewLocalCmd creates the `driverkit local` command.
func NewLocalCmd(rootCommand *RootCmd, rootOpts *RootOptions, rootFlags *pflag.FlagSet) *cobra.Command {
func NewLocalCmd(rootOpts *RootOptions, rootFlags *pflag.FlagSet) *cobra.Command {
opts := localCmdOptions{}
localCmd := &cobra.Command{
Use: "local",
Short: "Build Falco kernel modules and eBPF probes in local env with local kernel sources and gcc/clang.",
PersistentPreRunE: persistentPreRunFunc(rootCommand, rootOpts),
Use: "local",
Short: "Build Falco kernel modules and eBPF probes in local env with local kernel sources and gcc/clang.",
Run: func(c *cobra.Command, args []string) {
slog.With("processor", c.Name()).Info("driver building, it will take a few seconds")
if !configOptions.DryRun {
b := rootOpts.ToBuild()
if !b.HasOutputs() {
return
}
if opts.useDKMS {
currentUser, err := user.Current()
if err != nil {
slog.With("err", err.Error()).Error("Failed to retrieve user. Exiting.")
os.Exit(1)
}
if currentUser.Username != "root" {
slog.Error("Must be run as root for DKMS build.")
os.Exit(1)
}
}
if err := driverbuilder.NewLocalBuildProcessor(viper.GetInt("timeout"), opts.useDKMS, opts.srcDir, opts.envMap).Start(b); err != nil {
if err := driverbuilder.NewLocalBuildProcessor(viper.GetInt("timeout"),
opts.useDKMS,
opts.downloadHeaders,
opts.srcDir,
opts.envMap).Start(b); err != nil {
slog.With("err", err.Error()).Error("exiting")
os.Exit(1)
}
Expand All @@ -52,7 +43,6 @@ func NewLocalCmd(rootCommand *RootCmd, rootOpts *RootOptions, rootFlags *pflag.F
// Add root flags, but not the ones unneeded
unusedFlagsSet := map[string]struct{}{
"architecture": {},
"target": {},
"kernelurls": {},
"builderrepo": {},
"builderimage": {},
Expand All @@ -71,18 +61,9 @@ func NewLocalCmd(rootCommand *RootCmd, rootOpts *RootOptions, rootFlags *pflag.F
}
})
flagSet.BoolVar(&opts.useDKMS, "dkms", false, "Enforce usage of DKMS to build the kernel module.")
flagSet.BoolVar(&opts.downloadHeaders, "download-headers", false, "Try to automatically download kernel headers.")
flagSet.StringVar(&opts.srcDir, "src-dir", "", "Enforce usage of local source dir to build drivers.")
flagSet.StringToStringVar(&opts.envMap, "env", make(map[string]string), "Env variables to be enforced during the driver build.")
localCmd.PersistentFlags().AddFlagSet(flagSet)
return localCmd
}

// Partially overrides rootCmd.persistentPreRunFunc setting some defaults before config init/validation stage.
func persistentPreRunFunc(rootCommand *RootCmd, rootOpts *RootOptions) func(c *cobra.Command, args []string) error {
return func(c *cobra.Command, args []string) error {
// Default values
rootOpts.Target = "local"
rootOpts.Architecture = runtime.GOARCH
return rootCommand.c.PersistentPreRunE(c, args)
}
}
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func NewRootCmd() *RootCmd {
rootCmd.AddCommand(NewKubernetesCmd(rootOpts, flags))
rootCmd.AddCommand(NewKubernetesInClusterCmd(rootOpts, flags))
rootCmd.AddCommand(NewDockerCmd(rootOpts, flags))
rootCmd.AddCommand(NewLocalCmd(ret, rootOpts, flags))
rootCmd.AddCommand(NewLocalCmd(rootOpts, flags))
rootCmd.AddCommand(NewImagesCmd(rootOpts, flags))
rootCmd.AddCommand(NewCompletionCmd())

Expand Down
74 changes: 62 additions & 12 deletions pkg/driverbuilder/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,39 @@ import (
"bufio"
"context"
_ "embed"
"errors"
"fmt"
"github.com/falcosecurity/driverkit/pkg/driverbuilder/builder"
"io"
"log/slog"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"time"
)

const LocalBuildProcessorName = "local"
const (
LocalBuildProcessorName = "local"
kernelDirEnv = "KERNELDIR"
)

type LocalBuildProcessor struct {
timeout int
useDKMS bool
srcDir string
envMap map[string]string
timeout int
useDKMS bool
downloadHeaders bool
srcDir string
envMap map[string]string
}

func NewLocalBuildProcessor(timeout int, useDKMS bool, srcDir string, envMap map[string]string) *LocalBuildProcessor {
func NewLocalBuildProcessor(timeout int, useDKMS, downloadHeaders bool, srcDir string, envMap map[string]string) *LocalBuildProcessor {
return &LocalBuildProcessor{
timeout: timeout,
useDKMS: useDKMS,
srcDir: srcDir,
envMap: envMap,
timeout: timeout,
useDKMS: useDKMS,
srcDir: srcDir,
envMap: envMap,
downloadHeaders: downloadHeaders,
}
}

Expand All @@ -38,10 +46,51 @@ func (lbp *LocalBuildProcessor) String() string {

func (lbp *LocalBuildProcessor) Start(b *builder.Build) error {
slog.Debug("doing a new local build")


if lbp.useDKMS {
currentUser, err := user.Current()
if err != nil {
return err
}
if currentUser.Username != "root" {
return errors.New("must be run as root for DKMS build")
}
}

// We don't want to download headers
kr := b.KernelReleaseFromBuildConfig()

if lbp.downloadHeaders {
// Download headers for current distro
realBuilder, err := builder.Factory(b.TargetType)
// Since this can be used by external projects, it is not an issue
// if an unsupported target is passed.
// Go on skipping automatic kernel headers download.
if err == nil {
slog.Info("Trying automatic kernel headers download.")
kernelDownloadScript, err := builder.KernelDownloadScript(realBuilder, nil, kr)
if err == nil {
out, err := exec.Command("bash", "-c", kernelDownloadScript).Output()
if err == nil {
path := strings.TrimSuffix(string(out), "\n")
// add the kerneldir path to env
lbp.envMap[kernelDirEnv] = path
defer func() {
_ = os.RemoveAll("/tmp/kernel-download")
_ = os.RemoveAll(path)
}()
} else {
slog.Warn("Failed to download headers.", "err", err)
}
}
} else {
slog.Info("Skipping kernel headers automatic download.", "err", err)
}
}

// From now on, we use the local builder
b.TargetType = LocalBuildProcessorName

// create a builder based on the choosen build type
v, err := builder.Factory(b.TargetType)
if err != nil {
Expand Down Expand Up @@ -88,6 +137,7 @@ func (lbp *LocalBuildProcessor) Start(b *builder.Build) error {
srcProbePath := vv.GetProbeFullPath(c)

if len(lbp.srcDir) == 0 {
slog.Info("Downloading driver sources.")
// Download src!
libsDownloadScript, err := builder.LibsDownloadScript(c)
if err != nil {
Expand Down Expand Up @@ -117,11 +167,11 @@ func (lbp *LocalBuildProcessor) Start(b *builder.Build) error {
}

stdout, err := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout // redirect stderr to stdout so that we catch it
if err != nil {
slog.Warn("Failed to pipe stdout. Trying without piping.", "err", err)
_, err = cmd.CombinedOutput()
} else {
cmd.Stderr = cmd.Stdout // redirect stderr to stdout so that we catch it
defer stdout.Close()
err = cmd.Start()
if err != nil {
Expand Down

0 comments on commit cd9e5b3

Please sign in to comment.