189 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			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)
 | |
| }
 |