17 Commits

Author SHA1 Message Date
mae
33e11856c6 cmd/pkgserver: add status endpoint 2026-03-09 04:09:18 -05:00
mae
0f944f7a0e cmd/pkgserver: add createPackageIndex 2026-03-09 01:27:46 -05:00
mae
223037e7c2 cmd/pkgserver: add command handler 2026-03-08 22:28:08 -05:00
mae
acecad7f75 Merge remote-tracking branch 'origin/pkgserver' into pkgserver 2026-03-08 13:29:49 -05:00
mae
4f82f28c73 cmd/pkgserver: replace favicon 2026-03-08 13:29:15 -05:00
mae
ae07e0127b cmd/pkgserver: pagination 2026-03-08 13:29:15 -05:00
mae
d2696a6f30 cmd/pkgserver: basic web ui 2026-03-08 13:29:10 -05:00
mae
17ba70771c cmd/pkgserver: replace favicon 2026-03-08 13:29:01 -05:00
mae
93984f29da cmd/pkgserver: pagination 2026-03-08 13:29:01 -05:00
mae
d7cd746b43 cmd/pkgserver: basic web ui 2026-03-08 13:29:01 -05:00
mae
b255f07b0f Merge remote-tracking branch 'origin/pkgserver' into pkgserver 2026-03-05 02:06:27 -06:00
mae
dec4cdd068 cmd/pkgserver: replace favicon 2026-03-05 02:06:07 -06:00
mae
73c620ecd5 cmd/pkgserver: pagination 2026-03-05 02:06:07 -06:00
mae
69467a1542 cmd/pkgserver: basic web ui 2026-03-05 02:06:07 -06:00
mae
1ae6a35bc8 cmd/pkgserver: replace favicon 2026-03-05 01:12:17 -06:00
mae
9ef5b52b85 cmd/pkgserver: pagination 2026-03-05 00:32:25 -06:00
mae
f93158cb3c cmd/pkgserver: basic web ui 2026-03-04 22:50:58 -06:00
13 changed files with 34 additions and 280 deletions

View File

@@ -40,7 +40,7 @@ type (
AllowOrphan bool
// Scheduling policy to set via sched_setscheduler(2). The zero value
// skips this call. Supported policies are [SCHED_BATCH], [SCHED_IDLE].
SchedPolicy SchedPolicy
SchedPolicy int
// Cgroup fd, nil to disable.
Cgroup *int
// ExtraFiles passed through to initial process in the container, with
@@ -373,23 +373,12 @@ func (p *Container) Start() error {
// sched_setscheduler: thread-directed but acts on all processes
// created from the calling thread
if p.SchedPolicy > 0 && p.SchedPolicy <= _SCHED_LAST {
var param schedParam
if priority, err := p.SchedPolicy.GetPriorityMin(); err != nil {
return &StartError{
Fatal: true,
Step: "get minimum priority",
Err: err,
}
} else {
param.priority = priority
}
if p.SchedPolicy > 0 {
p.msg.Verbosef("setting scheduling policy %d", p.SchedPolicy)
if err := schedSetscheduler(
0, // calling thread
p.SchedPolicy,
&param,
&schedParam{0},
); err != nil {
return &StartError{
Fatal: true,

View File

@@ -1,9 +1,6 @@
package container
import (
"encoding"
"strconv"
"sync"
. "syscall"
"unsafe"
@@ -46,132 +43,18 @@ func Isatty(fd int) bool {
return r == 0
}
// SchedPolicy denotes a scheduling policy defined in include/uapi/linux/sched.h.
type SchedPolicy int
// include/uapi/linux/sched.h
const (
SCHED_NORMAL SchedPolicy = iota
SCHED_NORMAL = iota
SCHED_FIFO
SCHED_RR
SCHED_BATCH
_SCHED_ISO // SCHED_ISO: reserved but not implemented yet
_ // SCHED_ISO: reserved but not implemented yet
SCHED_IDLE
SCHED_DEADLINE
SCHED_EXT
_SCHED_LAST SchedPolicy = iota - 1
)
var _ encoding.TextMarshaler = _SCHED_LAST
var _ encoding.TextUnmarshaler = new(_SCHED_LAST)
// String returns a unique representation of policy, also used in encoding.
func (policy SchedPolicy) String() string {
switch policy {
case SCHED_NORMAL:
return ""
case SCHED_FIFO:
return "fifo"
case SCHED_RR:
return "rr"
case SCHED_BATCH:
return "batch"
case SCHED_IDLE:
return "idle"
case SCHED_DEADLINE:
return "deadline"
case SCHED_EXT:
return "ext"
default:
return "invalid policy " + strconv.Itoa(int(policy))
}
}
// MarshalText performs bounds checking and returns the result of String.
func (policy SchedPolicy) MarshalText() ([]byte, error) {
if policy == _SCHED_ISO || policy < 0 || policy > _SCHED_LAST {
return nil, EINVAL
}
return []byte(policy.String()), nil
}
// InvalidSchedPolicyError is an invalid string representation of a [SchedPolicy].
type InvalidSchedPolicyError string
func (InvalidSchedPolicyError) Unwrap() error { return EINVAL }
func (e InvalidSchedPolicyError) Error() string {
return "invalid scheduling policy " + strconv.Quote(string(e))
}
// UnmarshalText is the inverse of MarshalText.
func (policy *SchedPolicy) UnmarshalText(text []byte) error {
switch string(text) {
case "fifo":
*policy = SCHED_FIFO
case "rr":
*policy = SCHED_RR
case "batch":
*policy = SCHED_BATCH
case "idle":
*policy = SCHED_IDLE
case "deadline":
*policy = SCHED_DEADLINE
case "ext":
*policy = SCHED_EXT
case "":
*policy = 0
return nil
default:
return InvalidSchedPolicyError(text)
}
return nil
}
// for sched_get_priority_max and sched_get_priority_min
var (
schedPriority [_SCHED_LAST + 1][2]std.Int
schedPriorityErr [_SCHED_LAST + 1][2]error
schedPriorityOnce [_SCHED_LAST + 1][2]sync.Once
)
// GetPriorityMax returns the maximum priority value that can be used with the
// scheduling algorithm identified by policy.
func (policy SchedPolicy) GetPriorityMax() (std.Int, error) {
schedPriorityOnce[policy][0].Do(func() {
priority, _, errno := Syscall(
SYS_SCHED_GET_PRIORITY_MAX,
uintptr(policy),
0, 0,
)
schedPriority[policy][0] = std.Int(priority)
if schedPriority[policy][0] < 0 {
schedPriorityErr[policy][0] = errno
}
})
return schedPriority[policy][0], schedPriorityErr[policy][0]
}
// GetPriorityMin returns the minimum priority value that can be used with the
// scheduling algorithm identified by policy.
func (policy SchedPolicy) GetPriorityMin() (std.Int, error) {
schedPriorityOnce[policy][1].Do(func() {
priority, _, errno := Syscall(
SYS_SCHED_GET_PRIORITY_MIN,
uintptr(policy),
0, 0,
)
schedPriority[policy][1] = std.Int(priority)
if schedPriority[policy][1] < 0 {
schedPriorityErr[policy][1] = errno
}
})
return schedPriority[policy][1], schedPriorityErr[policy][1]
}
// schedParam is equivalent to struct sched_param from include/linux/sched.h.
type schedParam struct {
// sched_priority
@@ -191,7 +74,7 @@ type schedParam struct {
// this if you do not have something similar in place!
//
// [very subtle to use correctly]: https://www.openwall.com/lists/musl/2016/03/01/4
func schedSetscheduler(tid int, policy SchedPolicy, param *schedParam) error {
func schedSetscheduler(tid, policy int, param *schedParam) error {
if r, _, errno := Syscall(
SYS_SCHED_SETSCHEDULER,
uintptr(tid),

View File

@@ -1,100 +0,0 @@
package container_test
import (
"encoding/json"
"errors"
"math"
"reflect"
"syscall"
"testing"
"hakurei.app/container"
"hakurei.app/container/std"
)
func TestSchedPolicyJSON(t *testing.T) {
t.Parallel()
testCases := []struct {
policy container.SchedPolicy
want string
encodeErr error
decodeErr error
}{
{container.SCHED_NORMAL, `""`, nil, nil},
{container.SCHED_FIFO, `"fifo"`, nil, nil},
{container.SCHED_RR, `"rr"`, nil, nil},
{container.SCHED_BATCH, `"batch"`, nil, nil},
{4, `"invalid policy 4"`, syscall.EINVAL, container.InvalidSchedPolicyError("invalid policy 4")},
{container.SCHED_IDLE, `"idle"`, nil, nil},
{container.SCHED_DEADLINE, `"deadline"`, nil, nil},
{container.SCHED_EXT, `"ext"`, nil, nil},
{math.MaxInt, `"iso"`, syscall.EINVAL, container.InvalidSchedPolicyError("iso")},
}
for _, tc := range testCases {
name := tc.policy.String()
if tc.policy == container.SCHED_NORMAL {
name = "normal"
}
t.Run(name, func(t *testing.T) {
t.Parallel()
got, err := json.Marshal(tc.policy)
if !errors.Is(err, tc.encodeErr) {
t.Fatalf("Marshal: error = %v, want %v", err, tc.encodeErr)
}
if err == nil && string(got) != tc.want {
t.Fatalf("Marshal: %s, want %s", string(got), tc.want)
}
var v container.SchedPolicy
if err = json.Unmarshal([]byte(tc.want), &v); !reflect.DeepEqual(err, tc.decodeErr) {
t.Fatalf("Unmarshal: error = %v, want %v", err, tc.decodeErr)
}
if err == nil && v != tc.policy {
t.Fatalf("Unmarshal: %d, want %d", v, tc.policy)
}
})
}
}
func TestSchedPolicyMinMax(t *testing.T) {
t.Parallel()
testCases := []struct {
policy container.SchedPolicy
min, max std.Int
err error
}{
{container.SCHED_NORMAL, 0, 0, nil},
{container.SCHED_FIFO, 1, 99, nil},
{container.SCHED_RR, 1, 99, nil},
{container.SCHED_BATCH, 0, 0, nil},
{4, -1, -1, syscall.EINVAL},
{container.SCHED_IDLE, 0, 0, nil},
{container.SCHED_DEADLINE, 0, 0, nil},
{container.SCHED_EXT, 0, 0, nil},
}
for _, tc := range testCases {
name := tc.policy.String()
if tc.policy == container.SCHED_NORMAL {
name = "normal"
}
t.Run(name, func(t *testing.T) {
t.Parallel()
if priority, err := tc.policy.GetPriorityMax(); !reflect.DeepEqual(err, tc.err) {
t.Fatalf("GetPriorityMax: error = %v, want %v", err, tc.err)
} else if priority != tc.max {
t.Fatalf("GetPriorityMax: %d, want %d", priority, tc.max)
}
if priority, err := tc.policy.GetPriorityMin(); !reflect.DeepEqual(err, tc.err) {
t.Fatalf("GetPriorityMin: error = %v, want %v", err, tc.err)
} else if priority != tc.min {
t.Fatalf("GetPriorityMin: %d, want %d", priority, tc.min)
}
})
}
}

12
flake.lock generated
View File

@@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1772985280,
"narHash": "sha256-FdrNykOoY9VStevU4zjSUdvsL9SzJTcXt4omdEDZDLk=",
"lastModified": 1765384171,
"narHash": "sha256-FuFtkJrW1Z7u+3lhzPRau69E0CNjADku1mLQQflUORo=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "8f736f007139d7f70752657dff6a401a585d6cbc",
"rev": "44777152652bc9eacf8876976fa72cc77ca8b9d8",
"type": "github"
},
"original": {
@@ -23,11 +23,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1772822230,
"narHash": "sha256-yf3iYLGbGVlIthlQIk5/4/EQDZNNEmuqKZkQssMljuw=",
"lastModified": 1765311797,
"narHash": "sha256-mSD5Ob7a+T2RNjvPvOA1dkJHGVrNVl8ZOrAwBjKBDQo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "71caefce12ba78d84fe618cf61644dce01cf3a96",
"rev": "09eb77e94fa25202af8f3e81ddc7353d9970ac1b",
"type": "github"
},
"original": {

View File

@@ -99,7 +99,7 @@
hakurei = pkgs.pkgsStatic.callPackage ./package.nix {
inherit (pkgs)
# passthru.buildInputs
go_1_26
go
clang
# nativeBuildInputs
@@ -182,7 +182,7 @@
let
# this is used for interactive vm testing during development, where tests might be broken
package = self.packages.${pkgs.stdenv.hostPlatform.system}.hakurei.override {
buildGo126Module = previousArgs: pkgs.pkgsStatic.buildGo126Module (previousArgs // { doCheck = false; });
buildGoModule = previousArgs: pkgs.pkgsStatic.buildGoModule (previousArgs // { doCheck = false; });
};
in
{

2
go.mod
View File

@@ -1,3 +1,3 @@
module hakurei.app
go 1.26
go 1.25

View File

@@ -40,7 +40,7 @@ type ExecPath struct {
}
// SchedPolicy is the [container] scheduling policy.
var SchedPolicy container.SchedPolicy
var SchedPolicy int
// PromoteLayers returns artifacts with identical-by-content layers promoted to
// the highest priority instance, as if mounted via [ExecPath].

View File

@@ -82,11 +82,6 @@ install -Dm0500 \
echo "Installing linux $1..."
cp -av "$2" "$4"
cp -av "$3" "$4"
`))),
pkg.Path(AbsUsrSrc.Append(
".depmod",
), false, pkg.NewFile("depmod", []byte(`#!/bin/sh
exec /system/sbin/depmod -m /lib/modules "$@"
`))),
},
@@ -1215,11 +1210,6 @@ cgit 1.2.3-korg
"all",
},
Install: `
# kernel is not aware of kmod moduledir
install -Dm0500 \
/usr/src/.depmod \
/sbin/depmod
make \
"-j$(nproc)" \
-f /usr/src/kernel/Makefile \
@@ -1227,10 +1217,9 @@ make \
LLVM=1 \
INSTALL_PATH=/work \
install \
INSTALL_MOD_PATH=/work/system \
DEPMOD=/sbin/depmod \
INSTALL_MOD_PATH=/work \
modules_install
rm -v /work/system/lib/modules/` + kernelVersion + `/build
rm -v /work/lib/modules/` + kernelVersion + `/build
`,
},
Flex,

View File

@@ -14,7 +14,6 @@ func (t Toolchain) newKmod() (pkg.Artifact, string) {
pkg.TarGzip,
), nil, &MesonHelper{
Setup: [][2]string{
{"Dmoduledir", "/system/lib/modules"},
{"Dsysconfdir", "/system/etc"},
{"Dbashcompletiondir", "no"},
{"Dfishcompletiondir", "no"},

View File

@@ -125,8 +125,6 @@ func (t Toolchain) newLLVMVariant(variant string, attr *llvmAttr) pkg.Artifact {
[2]string{"LLVM_INSTALL_BINUTILS_SYMLINKS", "ON"},
[2]string{"LLVM_INSTALL_CCTOOLS_SYMLINKS", "ON"},
[2]string{"LLVM_LIT_ARGS", "'--verbose'"},
)
}

View File

@@ -1,7 +1,7 @@
{
lib,
stdenv,
buildGo126Module,
buildGoModule,
makeBinaryWrapper,
xdg-dbus-proxy,
pkg-config,
@@ -17,7 +17,7 @@
fuse3,
# for passthru.buildInputs
go_1_26,
go,
clang,
# for check
@@ -28,7 +28,7 @@
withStatic ? stdenv.hostPlatform.isStatic,
}:
buildGo126Module rec {
buildGoModule rec {
pname = "hakurei";
version = "0.3.6";
@@ -51,7 +51,7 @@ buildGo126Module rec {
];
nativeBuildInputs = [
go_1_26
go
pkg-config
wayland-scanner
];
@@ -125,20 +125,16 @@ buildGo126Module rec {
--inherit-argv0 --prefix PATH : ${lib.makeBinPath appPackages}
'';
passthru = {
go = go_1_26;
passthru.targetPkgs = [
go
clang
xorg.xorgproto
util-linux
targetPkgs = [
go_1_26
clang
xorg.xorgproto
util-linux
# for go generate
wayland-protocols
wayland-scanner
]
++ buildInputs
++ nativeBuildInputs;
};
# for go generate
wayland-protocols
wayland-scanner
]
++ buildInputs
++ nativeBuildInputs;
}

View File

@@ -34,7 +34,7 @@ testers.nixosTest {
(writeShellScriptBin "hakurei-test" ''
# Assert hst CGO_ENABLED=0: ${
with pkgs;
runCommand "hakurei-hst-cgo" { nativeBuildInputs = [ self.packages.${system}.hakurei.go ]; } ''
runCommand "hakurei-hst-cgo" { nativeBuildInputs = [ go ]; } ''
cp -r ${options.environment.hakurei.package.default.src} "$out"
chmod -R +w "$out"
cp ${writeText "hst_cgo_test.go" ''package hakurei_test;import("testing";"hakurei.app/hst");func TestTemplate(t *testing.T){hst.Template()}''} "$out/hst_cgo_test.go"