-
Notifications
You must be signed in to change notification settings - Fork 4
/
internal.go
139 lines (117 loc) · 3.79 KB
/
internal.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package server
import (
"fmt"
"os/exec"
"strings"
"github.com/roadrunner-server/errors"
"github.com/roadrunner-server/pool/ipc/pipe"
"github.com/roadrunner-server/pool/ipc/socket"
"github.com/roadrunner-server/pool/pool"
"github.com/roadrunner-server/pool/process"
"github.com/roadrunner-server/tcplisten"
"go.uber.org/zap"
)
type internalCommand func() *exec.Cmd
// should be the same as pool.Command
type internalCmdWithArgs func(command []string) *exec.Cmd
// cmdFactory provides worker command factory associated with given context
func (p *Plugin) cmdFactory(env map[string]string) internalCommand {
return func() *exec.Cmd {
var cmd *exec.Cmd
if len(p.preparedCmd) == 1 {
cmd = exec.Command(p.preparedCmd[0])
} else {
cmd = exec.Command(p.preparedCmd[0], p.preparedCmd[1:]...)
}
// copy prepared envs
cmd.Env = make([]string, len(p.preparedEnvs))
copy(cmd.Env, p.preparedEnvs)
// append external envs
if len(env) > 0 {
for k, v := range env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", strings.ToUpper(k), v))
}
}
process.IsolateProcess(cmd)
// if the user is not empty, and the OS is linux or macOS,
// execute php worker from that particular user
if p.cfg.User != "" {
err := process.ExecuteFromUser(cmd, p.cfg.User)
if err != nil {
panic(fmt.Errorf("RoadRunner can't execute process from the specified user: %s, error: %w", p.cfg.User, err))
}
}
return cmd
}
}
// customCmd used as and enhancement for the CmdFactory to use with a custom command string (used by default)
func (p *Plugin) customCmd(env map[string]string) internalCmdWithArgs {
return func(command []string) *exec.Cmd {
// if no command provided, use the server's one
if len(command) == 0 {
command = p.cfg.Command
}
var cmd *exec.Cmd
preparedCmd := make([]string, 0, 5)
// here we may have 2 cases: command declared as a space-separated string or as a slice
switch len(command) {
// command defined as a space-separated string
case 1:
// we know that the len is 1, so we can safely use the first element
preparedCmd = append(preparedCmd, strings.Split(command[0], " ")...)
default:
// we have a slice with a 2 or more elements
// first element is the command, the rest are arguments
preparedCmd = command
}
if len(preparedCmd) == 1 {
cmd = exec.Command(preparedCmd[0])
} else {
cmd = exec.Command(preparedCmd[0], preparedCmd[1:]...)
}
// copy prepared envs
cmd.Env = make([]string, len(p.preparedEnvs))
copy(cmd.Env, p.preparedEnvs)
// append external envs
if len(env) > 0 {
for k, v := range env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", strings.ToUpper(k), v))
}
}
process.IsolateProcess(cmd)
// if a user is not empty, and the OS is linux or macOS,
// execute php worker from that particular user
if p.cfg.User != "" {
err := process.ExecuteFromUser(cmd, p.cfg.User)
if err != nil {
p.log.Panic("can't execute command from the user", zap.String("user", p.cfg.User), zap.Error(err))
return nil
}
}
return cmd
}
}
// creates relay and worker factory.
func initFactory(log *zap.Logger, relay string) (pool.Factory, error) {
const op = errors.Op("server_plugin_init_factory")
if relay == "" || relay == pipes {
return pipe.NewPipeFactory(log), nil
}
dsn := strings.Split(relay, delim)
if len(dsn) != 2 {
return nil, errors.E(op, errors.Network, errors.Str("invalid DSN (tcp://:6001, unix://file.sock)"))
}
lsn, err := tcplisten.CreateListener(relay)
if err != nil {
return nil, errors.E(op, errors.Network, err)
}
switch dsn[0] {
// sockets group
case unix:
return socket.NewSocketServer(lsn, log), nil
case tcp:
return socket.NewSocketServer(lsn, log), nil
default:
return nil, errors.E(op, errors.Network, errors.Str("invalid DSN (tcp://:6001, unix://file.sock)"))
}
}