mirror of
https://gitea.plemya-x.ru/Plemya-x/ALR.git
synced 2025-01-10 17:26:45 +00:00
114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"plemya-x.ru/fakeroot"
|
|
"mvdan.cc/sh/v3/expand"
|
|
"mvdan.cc/sh/v3/interp"
|
|
)
|
|
|
|
// FakerootExecHandler was extracted from github.com/mvdan/sh/interp/handler.go
|
|
// and modified to run commands in a fakeroot environent.
|
|
func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc {
|
|
return func(ctx context.Context, args []string) error {
|
|
hc := interp.HandlerCtx(ctx)
|
|
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
|
if err != nil {
|
|
fmt.Fprintln(hc.Stderr, err)
|
|
return interp.NewExitStatus(127)
|
|
}
|
|
cmd := &exec.Cmd{
|
|
Path: path,
|
|
Args: args,
|
|
Env: execEnv(hc.Env),
|
|
Dir: hc.Dir,
|
|
Stdin: hc.Stdin,
|
|
Stdout: hc.Stdout,
|
|
Stderr: hc.Stderr,
|
|
}
|
|
|
|
err = fakeroot.Apply(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = cmd.Start()
|
|
if err == nil {
|
|
if done := ctx.Done(); done != nil {
|
|
go func() {
|
|
<-done
|
|
|
|
if killTimeout <= 0 || runtime.GOOS == "windows" {
|
|
_ = cmd.Process.Signal(os.Kill)
|
|
return
|
|
}
|
|
|
|
// TODO: don't temporarily leak this goroutine
|
|
// if the program stops itself with the
|
|
// interrupt.
|
|
go func() {
|
|
time.Sleep(killTimeout)
|
|
_ = cmd.Process.Signal(os.Kill)
|
|
}()
|
|
_ = cmd.Process.Signal(os.Interrupt)
|
|
}()
|
|
}
|
|
|
|
err = cmd.Wait()
|
|
}
|
|
|
|
switch x := err.(type) {
|
|
case *exec.ExitError:
|
|
// started, but errored - default to 1 if OS
|
|
// doesn't have exit statuses
|
|
if status, ok := x.Sys().(syscall.WaitStatus); ok {
|
|
if status.Signaled() {
|
|
if ctx.Err() != nil {
|
|
return ctx.Err()
|
|
}
|
|
return interp.NewExitStatus(uint8(128 + status.Signal()))
|
|
}
|
|
return interp.NewExitStatus(uint8(status.ExitStatus()))
|
|
}
|
|
return interp.NewExitStatus(1)
|
|
case *exec.Error:
|
|
// did not start
|
|
fmt.Fprintf(hc.Stderr, "%v\n", err)
|
|
return interp.NewExitStatus(127)
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// execEnv was extracted from github.com/mvdan/sh/interp/vars.go
|
|
func execEnv(env expand.Environ) []string {
|
|
list := make([]string, 0, 64)
|
|
env.Each(func(name string, vr expand.Variable) bool {
|
|
if !vr.IsSet() {
|
|
// If a variable is set globally but unset in the
|
|
// runner, we need to ensure it's not part of the final
|
|
// list. Seems like zeroing the element is enough.
|
|
// This is a linear search, but this scenario should be
|
|
// rare, and the number of variables shouldn't be large.
|
|
for i, kv := range list {
|
|
if strings.HasPrefix(kv, name+"=") {
|
|
list[i] = ""
|
|
}
|
|
}
|
|
}
|
|
if vr.Exported && vr.Kind == expand.String {
|
|
list = append(list, name+"="+vr.String())
|
|
}
|
|
return true
|
|
})
|
|
return list
|
|
}
|