1
0
out-of-tree/main.go

189 lines
4.9 KiB
Go

// Copyright 2023 Mikhail Klementev. All rights reserved.
// Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file.
package main
import (
"fmt"
"os"
"os/exec"
"runtime"
"runtime/debug"
"strings"
"github.com/natefinch/lumberjack"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/alecthomas/kong"
_ "code.dumpstack.io/tools/out-of-tree/distro/centos"
_ "code.dumpstack.io/tools/out-of-tree/distro/debian"
_ "code.dumpstack.io/tools/out-of-tree/distro/opensuse"
_ "code.dumpstack.io/tools/out-of-tree/distro/oraclelinux"
_ "code.dumpstack.io/tools/out-of-tree/distro/ubuntu"
"code.dumpstack.io/tools/out-of-tree/cache"
"code.dumpstack.io/tools/out-of-tree/cmd"
"code.dumpstack.io/tools/out-of-tree/config/dotfiles"
"code.dumpstack.io/tools/out-of-tree/container"
"code.dumpstack.io/tools/out-of-tree/fs"
)
type CLI struct {
cmd.Globals
Pew cmd.PewCmd `cmd:"" help:"build, run, and test module/exploit"`
Kernel cmd.KernelCmd `cmd:"" aliases:"kernels" help:"manipulate kernels"`
Debug cmd.DebugCmd `cmd:"" help:"debug environment"`
Log cmd.LogCmd `cmd:"" help:"query logs"`
Pack cmd.PackCmd `cmd:"" help:"exploit pack test"`
Gen cmd.GenCmd `cmd:"" help:"generate .out-of-tree.toml skeleton"`
Image cmd.ImageCmd `cmd:"" aliases:"images" help:"manage images"`
Container cmd.ContainerCmd `cmd:"" aliases:"containers" help:"manage containers"`
Distro cmd.DistroCmd `cmd:"" help:"distro-related helpers"`
Daemon cmd.DaemonCmd `cmd:"" help:"run daemon"`
Version VersionFlag `name:"version" help:"print version information and quit"`
LogLevel LogLevelFlag `enum:"trace,debug,info,warn,error" default:"info"`
ContainerRuntime string `enum:"podman,docker" default:"podman"`
}
func last(s []string) string {
return s[len(s)-1]
}
func debugLevel(pc uintptr, file string, line int) string {
function := runtime.FuncForPC(pc).Name()
if strings.Contains(function, ".") {
function = last(strings.Split(function, "."))
}
return function
}
func traceLevel(pc uintptr, file string, line int) string {
function := runtime.FuncForPC(pc).Name()
if strings.Contains(function, "/") {
function = last(strings.Split(function, "/"))
}
return fmt.Sprintf("%s:%s:%d", file, function, line)
}
type LogLevelFlag string
func (loglevel LogLevelFlag) AfterApply() error {
switch loglevel {
case "debug":
zerolog.CallerMarshalFunc = debugLevel
log.Logger = log.With().Caller().Logger()
case "trace":
zerolog.CallerMarshalFunc = traceLevel
log.Logger = log.With().Caller().Logger()
}
return nil
}
type VersionFlag string
func (v VersionFlag) Decode(ctx *kong.DecodeContext) error { return nil }
func (v VersionFlag) IsBool() bool { return true }
func (v VersionFlag) BeforeApply(app *kong.Kong, vars kong.Vars) error {
fmt.Println(vars["version"])
app.Exit(0)
return nil
}
func main() {
cli := CLI{}
ctx := kong.Parse(&cli,
kong.Name("out-of-tree"),
kong.Description("kernel {module, exploit} development tool"),
kong.UsageOnError(),
kong.ConfigureHelp(kong.HelpOptions{
Compact: true,
}),
kong.Vars{
"version": "2.1.2",
},
)
switch cli.LogLevel {
case "trace":
cmd.LogLevel = zerolog.TraceLevel
case "debug":
cmd.LogLevel = zerolog.DebugLevel
case "info":
cmd.LogLevel = zerolog.InfoLevel
case "warn":
cmd.LogLevel = zerolog.WarnLevel
case "error":
cmd.LogLevel = zerolog.ErrorLevel
}
cmd.ConsoleWriter = cmd.LevelWriter{Writer: zerolog.NewConsoleWriter(
func(w *zerolog.ConsoleWriter) {
w.Out = os.Stderr
},
),
Level: cmd.LogLevel,
}
cmd.FileWriter = cmd.LevelWriter{Writer: &lumberjack.Logger{
Filename: dotfiles.File("logs/out-of-tree.log"),
},
Level: zerolog.TraceLevel,
}
log.Logger = log.Output(zerolog.MultiLevelWriter(
&cmd.ConsoleWriter,
&cmd.FileWriter,
))
log.Trace().Msg("start out-of-tree")
log.Debug().Msgf("%v", os.Args)
log.Debug().Msgf("%v", cli)
if buildInfo, ok := debug.ReadBuildInfo(); ok {
log.Debug().Msgf("%v", buildInfo.GoVersion)
log.Debug().Msgf("%v", buildInfo.Settings)
}
path := dotfiles.Dir()
yes, err := fs.CaseInsensitive(path)
if err != nil {
log.Fatal().Err(err).Msg(path)
}
if yes {
log.Warn().Msg("case-insensitive file system not supported")
}
_, err = exec.LookPath(cli.ContainerRuntime)
if err != nil {
if cli.ContainerRuntime == "podman" { // default value
log.Debug().Msgf("podman is not found in $PATH, " +
"fall back to docker")
cli.ContainerRuntime = "docker"
}
_, err = exec.LookPath(cli.ContainerRuntime)
if err != nil {
log.Fatal().Msgf("%v is not found in $PATH",
cli.ContainerRuntime)
}
}
container.Runtime = cli.ContainerRuntime
if cli.Globals.CacheURL.String() != "" {
cache.URL = cli.Globals.CacheURL.String()
}
log.Debug().Msgf("set cache url to %s", cache.URL)
err = ctx.Run(&cli.Globals)
ctx.FatalIfErrorf(err)
}