-
Notifications
You must be signed in to change notification settings - Fork 4
/
init.go
117 lines (96 loc) · 2.38 KB
/
init.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package server
import (
"fmt"
"os"
"os/exec"
"strings"
"time"
"github.com/roadrunner-server/errors"
"github.com/roadrunner-server/pool/process"
"go.uber.org/zap"
)
type command struct {
log *zap.Logger
cfg *InitConfig
}
func newCommand(log *zap.Logger, cfg *InitConfig) *command {
return &command{
log: log,
cfg: cfg,
}
}
func (b *command) start() error {
const op = errors.Op("server_on_init")
stopCh := make(chan error, 1)
cmd := b.createProcess(b.cfg.Env, b.cfg.Command)
if b.cfg.User != "" {
err := process.ExecuteFromUser(cmd, b.cfg.User)
if err != nil {
return errors.E(op, err)
}
}
timer := time.NewTimer(b.cfg.ExecTimeout)
err := cmd.Start()
if err != nil {
return errors.E(op, err)
}
go func() {
errW := cmd.Wait()
if errW != nil {
b.log.Error("process wait", zap.Error(errW))
stopCh <- errW
return
}
stopCh <- nil
}()
select {
case <-timer.C:
err = cmd.Process.Kill()
if err != nil {
b.log.Error("process killed", zap.Error(err))
return err
}
return nil
case err := <-stopCh:
timer.Stop()
return err
}
}
func (b *command) Write(data []byte) (int, error) {
b.log.Info(string(data))
return len(data), nil
}
// create command for the process
func (b *command) createProcess(env map[string]string, cmd []string) *exec.Cmd {
// cmdArgs contain command arguments if the command in the form of: php <command> or ls <command> -i -b
var cmdArgs []string
var execCmd *exec.Cmd
// here we may have 2 cases: command declared as a space separated string or as a slice
switch len(cmd) {
// command defined as a space separated string
case 1:
// we know that the len is 1, so we can safely use the first element
cmdArgs = append(cmdArgs, strings.Split(cmd[0], " ")...)
default:
// we have a slice with a 2 or more elements
// first element is the command, the rest are arguments
cmdArgs = cmd
}
if len(cmdArgs) == 1 {
execCmd = exec.Command(cmd[0])
} else {
execCmd = exec.Command(cmdArgs[0], cmdArgs[1:]...)
}
// set env variables from the config
if len(env) > 0 {
for k, v := range env {
execCmd.Env = append(execCmd.Env, fmt.Sprintf("%s=%s", strings.ToUpper(k), os.Expand(v, os.Getenv)))
}
}
// append system envs
execCmd.Env = append(execCmd.Env, os.Environ()...)
// redirect stderr and stdout into the Write function of the process.go
execCmd.Stderr = b
execCmd.Stdout = b
return execCmd
}