feat: publish the Sandwich Hime source preview

Signed-off-by: Cole Speelman <gamertan@noreply.localhost>
This commit is contained in:
2026-08-11 20:15:06 -04:00
commit 9b29b3d7f8
100 changed files with 10989 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: AGPL-3.0-only
package devserver
import (
"context"
"errors"
"os"
"os/exec"
"strconv"
"sync"
"time"
)
type candidateProcess struct {
command *exec.Cmd
address string
binaryPath string
processTree uintptr
exited chan struct{}
mu sync.Mutex
waitErr error
}
func taskkillArguments(pid int, force bool) []string {
arguments := []string{"/PID", strconv.Itoa(pid), "/T"}
if force {
arguments = append(arguments, "/F")
}
return arguments
}
func startManagedProcess(command *exec.Cmd, address, binaryPath string) (*candidateProcess, error) {
configureProcess(command)
if err := command.Start(); err != nil {
return nil, err
}
processTree, err := attachProcessTree(command)
if err != nil {
// Never return an unmanaged child. In particular, a Windows candidate
// must be attached to its Job Object before it can be considered usable.
_ = killProcess(command, 0)
_ = command.Wait()
return nil, errors.New("attach managed process tree: " + err.Error())
}
candidate := &candidateProcess{
command: command,
address: address,
binaryPath: binaryPath,
processTree: processTree,
exited: make(chan struct{}),
}
go func() {
err := command.Wait()
candidate.mu.Lock()
candidate.waitErr = err
candidate.mu.Unlock()
close(candidate.exited)
}()
return candidate, nil
}
func (c *candidateProcess) result() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.waitErr
}
func (c *candidateProcess) hasExited() bool {
select {
case <-c.exited:
return true
default:
return false
}
}
func (c *candidateProcess) cleanupProcessTree() error {
c.mu.Lock()
processTree := c.processTree
c.processTree = 0
c.mu.Unlock()
return cleanupProcess(c.command, processTree)
}
func (c *candidateProcess) stop(ctx context.Context) error {
defer func() {
if c.binaryPath != "" {
_ = os.Remove(c.binaryPath)
}
}()
if c.hasExited() {
return errors.Join(acceptableStopError(c.result()), c.cleanupProcessTree())
}
if err := terminateProcess(c.command, c.processTree); err != nil {
// A graceful signal is best effort. Failure to deliver it immediately
// escalates to the platform's process-tree termination primitive.
_ = killProcess(c.command, c.processTree)
}
select {
case <-c.exited:
return errors.Join(acceptableStopError(c.result()), c.cleanupProcessTree())
case <-ctx.Done():
killErr := killProcess(c.command, c.processTree)
select {
case <-c.exited:
return errors.Join(ctx.Err(), killErr, acceptableStopError(c.result()), c.cleanupProcessTree())
case <-time.After(2 * time.Second):
// Closing a Windows Job Object configured with
// KILL_ON_JOB_CLOSE is the final bounded fallback. On Unix this
// repeats the process-group kill without retaining resources.
return errors.Join(ctx.Err(), killErr, c.cleanupProcessTree())
}
}
}
func acceptableStopError(err error) error {
if err == nil {
return nil
}
var exitError *exec.ExitError
if errors.As(err, &exitError) {
return nil
}
return err
}