aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-03-28 17:26:45 +0900
committerOphestra <cat@gensokyo.uk>2026-03-28 17:31:46 +0900
commite33294db9c334530582215b2c5dd23c0730bc162 (patch)
treeb2647a4c84a01ad39d4693f852326299198e3ee4
parentb1ea3b4acf85365fd76c890f8b8852b5f95be5c1 (diff)
cmd/hakurei: document stable behaviour
These are undocumented anywhere else and is required by tools invoking hakurei. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--cmd/hakurei/main.go39
-rw-r--r--cmd/hakurei/parse.go27
2 files changed, 57 insertions, 9 deletions
diff --git a/cmd/hakurei/main.go b/cmd/hakurei/main.go
index a5b14d9c..19ffdb68 100644
--- a/cmd/hakurei/main.go
+++ b/cmd/hakurei/main.go
@@ -1,3 +1,40 @@
+// 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 (
@@ -25,8 +62,8 @@ func main() {
// early init path, skips root check and duplicate PR_SET_DUMPABLE
container.TryArgv0(nil)
- log.SetPrefix("hakurei: ")
log.SetFlags(0)
+ log.SetPrefix("hakurei: ")
msg := message.New(log.Default())
early := earlyHardeningErrs{
diff --git a/cmd/hakurei/parse.go b/cmd/hakurei/parse.go
index 266eb47f..31d0dfa6 100644
--- a/cmd/hakurei/parse.go
+++ b/cmd/hakurei/parse.go
@@ -17,8 +17,9 @@ import (
)
// tryPath attempts to read [hst.Config] from multiple sources.
-// tryPath reads from [os.Stdin] if name has value "-".
-// Otherwise, name is passed to tryFd, and if that returns nil, name is passed to [os.Open].
+//
+// tryPath reads from [os.Stdin] if name has value "-". Otherwise, name is
+// passed to tryFd, and if that returns nil, name is passed to [os.Open].
func tryPath(msg message.Msg, name string) (config *hst.Config) {
var r io.ReadCloser
config = new(hst.Config)
@@ -46,7 +47,8 @@ func tryPath(msg message.Msg, name string) (config *hst.Config) {
return
}
-// tryFd returns a [io.ReadCloser] if name represents an integer corresponding to a valid file descriptor.
+// tryFd returns a [io.ReadCloser] if name represents an integer corresponding
+// to a valid file descriptor.
func tryFd(msg message.Msg, name string) io.ReadCloser {
if v, err := strconv.Atoi(name); err != nil {
if !errors.Is(err, strconv.ErrSyntax) {
@@ -60,7 +62,12 @@ func tryFd(msg message.Msg, name string) io.ReadCloser {
msg.Verbosef("trying config stream from %d", v)
fd := uintptr(v)
- if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_GETFD, 0); errno != 0 {
+ if _, _, errno := syscall.Syscall(
+ syscall.SYS_FCNTL,
+ fd,
+ syscall.F_GETFD,
+ 0,
+ ); errno != 0 {
if errors.Is(errno, syscall.EBADF) { // reject bad fd
return nil
}
@@ -75,10 +82,12 @@ func tryFd(msg message.Msg, name string) io.ReadCloser {
}
}
-// shortLengthMin is the minimum length a short form identifier can have and still be interpreted as an identifier.
+// shortLengthMin is the minimum length a short form identifier can have and
+// still be interpreted as an identifier.
const shortLengthMin = 1 << 3
-// shortIdentifier returns an eight character short representation of [hst.ID] from its random bytes.
+// shortIdentifier returns an eight character short representation of [hst.ID]
+// from its random bytes.
func shortIdentifier(id *hst.ID) string {
return shortIdentifierString(id.String())
}
@@ -88,7 +97,8 @@ func shortIdentifierString(s string) string {
return s[len(hst.ID{}) : len(hst.ID{})+shortLengthMin]
}
-// tryIdentifier attempts to match [hst.State] from a [hex] representation of [hst.ID] or a prefix of its lower half.
+// tryIdentifier attempts to match [hst.State] from a [hex] representation of
+// [hst.ID] or a prefix of its lower half.
func tryIdentifier(msg message.Msg, name string, s *store.Store) *hst.State {
const (
likeShort = 1 << iota
@@ -96,7 +106,8 @@ func tryIdentifier(msg message.Msg, name string, s *store.Store) *hst.State {
)
var likely uintptr
- if len(name) >= shortLengthMin && len(name) <= len(hst.ID{}) { // half the hex representation
+ // half the hex representation
+ if len(name) >= shortLengthMin && len(name) <= len(hst.ID{}) {
// cannot safely decode here due to unknown alignment
for _, c := range name {
if c >= '0' && c <= '9' {