ALR

Форк
1
114 строк · 2.6 Кб
1
package handlers
2

3
import (
4
	"context"
5
	"fmt"
6
	"os"
7
	"os/exec"
8
	"runtime"
9
	"strings"
10
	"syscall"
11
	"time"
12

13
	"plemya-x.ru/fakeroot"
14
	"mvdan.cc/sh/v3/expand"
15
	"mvdan.cc/sh/v3/interp"
16
)
17

18
// FakerootExecHandler was extracted from github.com/mvdan/sh/interp/handler.go
19
// and modified to run commands in a fakeroot environent.
20
func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc {
21
	return func(ctx context.Context, args []string) error {
22
		hc := interp.HandlerCtx(ctx)
23
		path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
24
		if err != nil {
25
			fmt.Fprintln(hc.Stderr, err)
26
			return interp.NewExitStatus(127)
27
		}
28
		cmd := &exec.Cmd{
29
			Path:   path,
30
			Args:   args,
31
			Env:    execEnv(hc.Env),
32
			Dir:    hc.Dir,
33
			Stdin:  hc.Stdin,
34
			Stdout: hc.Stdout,
35
			Stderr: hc.Stderr,
36
		}
37

38
		err = fakeroot.Apply(cmd)
39
		if err != nil {
40
			return err
41
		}
42

43
		err = cmd.Start()
44
		if err == nil {
45
			if done := ctx.Done(); done != nil {
46
				go func() {
47
					<-done
48

49
					if killTimeout <= 0 || runtime.GOOS == "windows" {
50
						_ = cmd.Process.Signal(os.Kill)
51
						return
52
					}
53

54
					// TODO: don't temporarily leak this goroutine
55
					// if the program stops itself with the
56
					// interrupt.
57
					go func() {
58
						time.Sleep(killTimeout)
59
						_ = cmd.Process.Signal(os.Kill)
60
					}()
61
					_ = cmd.Process.Signal(os.Interrupt)
62
				}()
63
			}
64

65
			err = cmd.Wait()
66
		}
67

68
		switch x := err.(type) {
69
		case *exec.ExitError:
70
			// started, but errored - default to 1 if OS
71
			// doesn't have exit statuses
72
			if status, ok := x.Sys().(syscall.WaitStatus); ok {
73
				if status.Signaled() {
74
					if ctx.Err() != nil {
75
						return ctx.Err()
76
					}
77
					return interp.NewExitStatus(uint8(128 + status.Signal()))
78
				}
79
				return interp.NewExitStatus(uint8(status.ExitStatus()))
80
			}
81
			return interp.NewExitStatus(1)
82
		case *exec.Error:
83
			// did not start
84
			fmt.Fprintf(hc.Stderr, "%v\n", err)
85
			return interp.NewExitStatus(127)
86
		default:
87
			return err
88
		}
89
	}
90
}
91

92
// execEnv was extracted from github.com/mvdan/sh/interp/vars.go
93
func execEnv(env expand.Environ) []string {
94
	list := make([]string, 0, 64)
95
	env.Each(func(name string, vr expand.Variable) bool {
96
		if !vr.IsSet() {
97
			// If a variable is set globally but unset in the
98
			// runner, we need to ensure it's not part of the final
99
			// list. Seems like zeroing the element is enough.
100
			// This is a linear search, but this scenario should be
101
			// rare, and the number of variables shouldn't be large.
102
			for i, kv := range list {
103
				if strings.HasPrefix(kv, name+"=") {
104
					list[i] = ""
105
				}
106
			}
107
		}
108
		if vr.Exported && vr.Kind == expand.String {
109
			list = append(list, name+"="+vr.String())
110
		}
111
		return true
112
	})
113
	return list
114
}
115

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.