Skip to content

Commit 2c7d6f0

Browse files
FedeDPpoiana
authored andcommitted
new(cmd,pkg/driverbuilder): support env,src-dir and dkms build options in local builder.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
1 parent 78bb75a commit 2c7d6f0

File tree

4 files changed

+116
-11
lines changed

4 files changed

+116
-11
lines changed

cmd/local.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@ import (
99
"golang.org/x/sys/unix"
1010
"log/slog"
1111
"os"
12+
"os/user"
1213
"runtime"
1314
"strings"
1415
)
1516

17+
type localCmdOptions struct {
18+
useDKMS bool
19+
srcDir string
20+
envMap map[string]string
21+
}
22+
1623
// NewLocalCmd creates the `driverkit local` command.
1724
func NewLocalCmd(rootCommand *RootCmd, rootOpts *RootOptions, rootFlags *pflag.FlagSet) *cobra.Command {
25+
opts := localCmdOptions{}
1826
localCmd := &cobra.Command{
1927
Use: "local",
2028
Short: "Build Falco kernel modules and eBPF probes in local env with local kernel sources and gcc/clang.",
@@ -26,7 +34,18 @@ func NewLocalCmd(rootCommand *RootCmd, rootOpts *RootOptions, rootFlags *pflag.F
2634
if !b.HasOutputs() {
2735
return
2836
}
29-
if err := driverbuilder.NewLocalBuildProcessor(viper.GetInt("timeout")).Start(b); err != nil {
37+
if opts.useDKMS {
38+
currentUser, err := user.Current()
39+
if err != nil {
40+
slog.With("err", err.Error()).Error("Failed to retrieve user. Exiting.")
41+
os.Exit(1)
42+
}
43+
if currentUser.Username != "root" {
44+
slog.Error("Must be run as root for DKMS build.")
45+
os.Exit(1)
46+
}
47+
}
48+
if err := driverbuilder.NewLocalBuildProcessor(viper.GetInt("timeout"), opts.useDKMS, opts.srcDir, opts.envMap).Start(b); err != nil {
3049
slog.With("err", err.Error()).Error("exiting")
3150
os.Exit(1)
3251
}
@@ -56,6 +75,9 @@ func NewLocalCmd(rootCommand *RootCmd, rootOpts *RootOptions, rootFlags *pflag.F
5675
flagSet.AddFlag(flag)
5776
}
5877
})
78+
flagSet.BoolVar(&opts.useDKMS, "dkms", false, "Enforce usage of DKMS to build the kernel module.")
79+
flagSet.StringVar(&opts.srcDir, "src-dir", "", "Enforce usage of local source dir to build drivers.")
80+
flagSet.StringToStringVar(&opts.envMap, "env", nil, "Env variables to be enforced during the driver build.")
5981
localCmd.PersistentFlags().AddFlagSet(flagSet)
6082
return localCmd
6183
}

pkg/driverbuilder/builder/local.go

+31-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ var localTemplate string
1515

1616
type LocalBuilder struct {
1717
GccPath string
18+
UseDKMS bool
19+
SrcDir string
1820
}
1921

2022
func (l *LocalBuilder) Name() string {
@@ -25,7 +27,7 @@ func (l *LocalBuilder) TemplateScript() string {
2527
return localTemplate
2628
}
2729

28-
func (l *LocalBuilder) URLs(kr kernelrelease.KernelRelease) ([]string, error) {
30+
func (l *LocalBuilder) URLs(_ kernelrelease.KernelRelease) ([]string, error) {
2931
return nil, nil
3032
}
3133

@@ -36,18 +38,43 @@ func (l *LocalBuilder) MinimumURLs() int {
3638

3739
type localTemplateData struct {
3840
commonTemplateData
41+
UseDKMS bool
42+
DownloadSrc bool
43+
DriverVersion string
44+
KernelRelease string
3945
}
4046

41-
func (l *LocalBuilder) TemplateData(c Config, _ kernelrelease.KernelRelease, _ []string) interface{} {
47+
func (l *LocalBuilder) TemplateData(c Config, kr kernelrelease.KernelRelease, _ []string) interface{} {
4248
return localTemplateData{
4349
commonTemplateData: commonTemplateData{
44-
DriverBuildDir: DriverDirectory,
50+
DriverBuildDir: l.GetDriverBuildDir(),
4551
ModuleDownloadURL: fmt.Sprintf("%s/%s.tar.gz", c.DownloadBaseURL, c.DriverVersion),
4652
ModuleDriverName: c.DriverName,
47-
ModuleFullPath: ModuleFullPath,
53+
ModuleFullPath: l.GetModuleFullPath(c, kr),
4854
BuildModule: len(c.ModuleFilePath) > 0,
4955
BuildProbe: len(c.ProbeFilePath) > 0,
5056
GCCVersion: l.GccPath,
5157
},
58+
UseDKMS: l.UseDKMS,
59+
DownloadSrc: len(l.SrcDir) == 0, // if no srcdir is provided, download src!
60+
DriverVersion: c.DriverVersion,
61+
KernelRelease: c.KernelRelease,
5262
}
5363
}
64+
65+
func (l *LocalBuilder) GetModuleFullPath(c Config, kr kernelrelease.KernelRelease) string {
66+
moduleFullPath := ModuleFullPath
67+
if l.UseDKMS {
68+
// When using dkms, we will use a GLOB to match the pattern; ModuleFullPath won't be used in the templated script anyway.
69+
moduleFullPath = fmt.Sprintf("/var/lib/dkms/%s/%s/%s/%s/module/%s.*", c.DriverName, c.DriverVersion, kr.String(), kr.Architecture.ToNonDeb(), c.DriverName)
70+
}
71+
return moduleFullPath
72+
}
73+
74+
func (l *LocalBuilder) GetDriverBuildDir() string {
75+
driverBuildDir := DriverDirectory
76+
if len(l.SrcDir) > 0 {
77+
driverBuildDir = l.SrcDir
78+
}
79+
return driverBuildDir
80+
}

pkg/driverbuilder/builder/templates/local.sh

+15
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#
2323
set -xeuo pipefail
2424

25+
{{ if .DownloadSrc }}
26+
echo "* Downloading driver sources"
2527
rm -Rf {{ .DriverBuildDir }}
2628
mkdir {{ .DriverBuildDir }}
2729
rm -Rf /tmp/module-download
@@ -32,8 +34,19 @@ mv /tmp/module-download/*/driver/* {{ .DriverBuildDir }}
3234

3335
cp /tmp/module-Makefile {{ .DriverBuildDir }}/Makefile
3436
bash /tmp/fill-driver-config.sh {{ .DriverBuildDir }}
37+
{{ end }}
3538

3639
{{ if .BuildModule }}
40+
{{ if .UseDKMS }}
41+
echo "* Building kmod with DKMS"
42+
# Build the module using DKMS
43+
echo "#!/usr/bin/env bash" > "/tmp/falco-dkms-make"
44+
echo "make CC={{ .GCCVersion }} \$@" >> "/tmp/falco-dkms-make"
45+
chmod +x "/tmp/falco-dkms-make"
46+
dkms install --directive="MAKE='/tmp/falco-dkms-make'" -m "{{ .ModuleDriverName }}" -v "{{ .DriverVersion }}" -k "{{ .KernelRelease }}"
47+
rm -Rf "/tmp/falco-dkms-make"
48+
{{ else }}
49+
echo "* Building kmod"
3750
# Build the module
3851
cd {{ .DriverBuildDir }}
3952
make CC={{ .GCCVersion }}
@@ -42,8 +55,10 @@ strip -g {{ .ModuleFullPath }}
4255
# Print results
4356
modinfo {{ .ModuleFullPath }}
4457
{{ end }}
58+
{{ end }}
4559

4660
{{ if .BuildProbe }}
61+
echo "* Building eBPF probe"
4762
# Build the eBPF probe
4863
cd {{ .DriverBuildDir }}/bpf
4964
make

pkg/driverbuilder/local.go

+47-6
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import (
77
_ "embed"
88
"fmt"
99
"github.com/falcosecurity/driverkit/pkg/driverbuilder/builder"
10+
"io"
1011
"log/slog"
1112
"os"
1213
"os/exec"
14+
"path"
1315
"path/filepath"
1416
"time"
1517
)
@@ -18,11 +20,17 @@ const LocalBuildProcessorName = "local"
1820

1921
type LocalBuildProcessor struct {
2022
timeout int
23+
useDKMS bool
24+
srcDir string
25+
envMap map[string]string
2126
}
2227

23-
func NewLocalBuildProcessor(timeout int) *LocalBuildProcessor {
28+
func NewLocalBuildProcessor(timeout int, useDKMS bool, srcDir string, envMap map[string]string) *LocalBuildProcessor {
2429
return &LocalBuildProcessor{
2530
timeout: timeout,
31+
useDKMS: useDKMS,
32+
srcDir: srcDir,
33+
envMap: envMap,
2634
}
2735
}
2836

@@ -104,6 +112,11 @@ func (lbp *LocalBuildProcessor) Start(b *builder.Build) error {
104112

105113
// Cannot fail
106114
vv, _ := v.(*builder.LocalBuilder)
115+
vv.SrcDir = lbp.srcDir
116+
vv.UseDKMS = lbp.useDKMS
117+
118+
modulePath := vv.GetModuleFullPath(c, kr)
119+
probePath := path.Join(vv.GetDriverBuildDir(), "bpf", builder.ProbeFileName)
107120
for _, gcc := range gccs {
108121
vv.GccPath = gcc
109122

@@ -115,6 +128,10 @@ func (lbp *LocalBuildProcessor) Start(b *builder.Build) error {
115128
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Duration(lbp.timeout)*time.Second)
116129
defer cancelFunc()
117130
cmd := exec.CommandContext(ctx, "/bin/bash", "-c", driverkitScript)
131+
// Append requested env variables to the command env
132+
for key, val := range lbp.envMap {
133+
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, val))
134+
}
118135
stdout, err := cmd.StdoutPipe()
119136
if err != nil {
120137
slog.Warn("Failed to pipe output. Trying without piping.", "err", err)
@@ -139,28 +156,52 @@ func (lbp *LocalBuildProcessor) Start(b *builder.Build) error {
139156
}
140157
// If we received an error, perhaps we must just rebuilt the kmod.
141158
// Check if we were able to build anything.
142-
if _, err = os.Stat(builder.ModuleFullPath); !os.IsNotExist(err) {
143-
// we built the kmod; there is no need to loop again.
159+
koFiles, err := filepath.Glob(modulePath)
160+
if err == nil && len(koFiles) > 0 {
144161
break
145162
}
146-
if _, err = os.Stat(builder.ProbeFullPath); !os.IsNotExist(err) {
163+
if _, err = os.Stat(probePath); !os.IsNotExist(err) {
147164
c.ProbeFilePath = ""
148165
}
149166
}
150167

151168
if len(b.ModuleFilePath) > 0 {
152-
if err = os.Rename(builder.ModuleFullPath, b.ModuleFilePath); err != nil {
169+
// If we received an error, perhaps we must just rebuilt the kmod.
170+
// Check if we were able to build anything.
171+
koFiles, err := filepath.Glob(modulePath)
172+
if err != nil || len(koFiles) == 0 {
173+
return fmt.Errorf("failed to find kernel module .ko file: %s", modulePath)
174+
}
175+
if err = copyDataToLocalPath(koFiles[0], b.ModuleFilePath); err != nil {
153176
return err
154177
}
155178
slog.With("path", b.ModuleFilePath).Info("kernel module available")
156179
}
157180

158181
if len(b.ProbeFilePath) > 0 {
159-
if err = os.Rename(builder.ProbeFullPath, b.ProbeFilePath); err != nil {
182+
if err = copyDataToLocalPath(probePath, b.ProbeFilePath); err != nil {
160183
return err
161184
}
162185
slog.With("path", b.ProbeFilePath).Info("eBPF probe available")
163186
}
164187

165188
return nil
166189
}
190+
191+
func copyDataToLocalPath(src, dest string) error {
192+
in, err := os.Open(filepath.Clean(src))
193+
if err != nil {
194+
return err
195+
}
196+
defer in.Close()
197+
err = os.MkdirAll(filepath.Dir(dest), 0o755)
198+
if err != nil {
199+
return err
200+
}
201+
out, err := os.OpenFile(filepath.Clean(dest), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755)
202+
if err == nil {
203+
defer out.Close()
204+
_, err = io.Copy(out, in)
205+
}
206+
return err
207+
}

0 commit comments

Comments
 (0)