// Hakurei runs user-specified containers as subordinate users.
//
// This program is generally invoked by another, higher level program, which
// creates container configuration via package [hst] or an implementation of it.
//
// The parent may leave files open and specify their file descriptor for various
// uses. In these cases, standard streams and netpoll files are treated as
// invalid file descriptors and rejected. All string representations must be in
// decimal.
//
// When specifying a [hst.Config] JSON stream or file to the run subcommand, the
// argument "-" is equivalent to stdin. Otherwise, file descriptor rules
// described above applies. Invalid file descriptors are treated as file names
// in their string representation, with the exception that if a netpoll file
// descriptor is attempted, the program fails.
//
// The flag --identifier-fd can be optionally specified to the run subcommand to
// receive the identifier of the newly started instance. File descriptor rules
// described above applies, and the file must be writable. This is sent after
// its state is made available, so the client must not attempt to poll for it.
// This uses the internal binary format of [hst.ID].
//
// For the show and ps subcommands, the flag --json can be applied to the main
// hakurei command to serialise output in JSON when applicable. Additionally,
// the flag --short targeting each subcommand is used to omit some information
// in both JSON and user-facing output. Only JSON-encoded output is covered
// under the compatibility promise.
//
// A template for [hst.Config] demonstrating all available configuration fields
// is returned by [hst.Template]. The JSON-encoded equivalent of this can be
// obtained via the template subcommand. Fields left unpopulated in the template
// (the direct_* family of fields, which are insecure under any configuration if
// enabled) are unsupported.
//
// For simple (but insecure) testing scenarios, the exec subcommand can be used
// to generate a simple, permissive configuration in-memory. See its help
// message for all available options.
package main

import (
	"context"
	_ "embed"
	"errors"
	"log"
	"os"
	"os/signal"
	"syscall"

	"hakurei.app/container"
	"hakurei.app/ext"
	"hakurei.app/message"
)

//go:generate cp ../../LICENSE .
//go:embed LICENSE
var license string

// earlyHardeningErrs are errors collected while setting up early hardening feature.
type earlyHardeningErrs struct{ yamaLSM, dumpable error }

func main() {
	// early init path, skips root check and duplicate PR_SET_DUMPABLE
	container.TryArgv0(nil)

	log.SetFlags(0)
	log.SetPrefix("hakurei: ")
	msg := message.New(log.Default())

	early := earlyHardeningErrs{
		yamaLSM:  ext.SetPtracer(0),
		dumpable: ext.SetDumpable(ext.SUID_DUMP_DISABLE),
	}

	if os.Geteuid() == 0 {
		log.Fatal("this program must not run as root")
	}

	ctx, stop := signal.NotifyContext(context.Background(),
		syscall.SIGINT, syscall.SIGTERM)
	defer stop() // unreachable

	buildCommand(ctx, msg, &early, os.Stderr).MustParse(os.Args[1:], func(err error) {
		msg.Verbosef("command returned %v", err)
		if errors.Is(err, errSuccess) {
			msg.BeforeExit()
			os.Exit(0)
		}
		// this catches faulty command handlers that fail to return before this point
	})
	log.Fatal("unreachable")
}
