summaryrefslogtreecommitdiff
path: root/server_config.go
blob: 7e8aa80ecfe88cf2cae4f3973b5e7e8e426b6f13 (plain)
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
package roadrunner

import (
	"errors"
	"net"
	"strings"
	"time"
	"os/exec"
	"syscall"
	"os/user"
	"strconv"
)

const (
	FactoryPipes  = iota
	FactorySocket
)

// Server config combines factory, pool and cmd configurations.
type ServerConfig struct {
	// Command includes command strings with all the parameters, example: "php worker.php pipes". This config section
	//	// must not change on re-configuration.
	Command string

	// User specifies what user to run command under, for Unix systems only. Support both UID and name options. Keep
	// empty to use current user.This config section must not change on re-configuration.
	User string

	// Group specifies what group to run command under, for Unix systems only. Support GID or name options. Keep empty
	// to use current user.This config section must not change on re-configuration.
	Group string

	// Relay defines connection method and factory to be used to connect to workers:
	// "pipes", "tcp://:6001", "unix://rr.sock"
	// This config section must not change on re-configuration.
	Relay string

	// RelayTimeout defines for how long socket factory will be waiting for worker connection. This config section
	// must not change on re-configuration.
	RelayTimeout time.Duration

	// Pool defines worker pool configuration, number of workers, timeouts and etc. This config section might change
	// while server is running.
	Pool *Config
}

// Differs returns true if configuration has changed but ignores pool changes.
func (cfg *ServerConfig) Differs(new *ServerConfig) bool {
	// command configuration has changed
	if cfg.Command != new.Command || cfg.User != new.User || cfg.Group != new.Group {
		return true
	}

	// factory configuration has changed
	return cfg.Relay != new.Relay || cfg.RelayTimeout != new.RelayTimeout
}

// makeCommands returns new command provider based on configured options.
func (cfg *ServerConfig) makeCommand() (func() *exec.Cmd, error) {
	var (
		err error
		u   *user.User
		g   *user.Group
		crd *syscall.Credential
		cmd = strings.Split(cfg.Command, " ")
	)

	if cfg.User != "" {
		if u, err = resolveUser(cfg.User); err != nil {
			return nil, err
		}
	}

	if cfg.Group != "" {
		if g, err = resolveGroup(cfg.Group); err != nil {
			return nil, err
		}
	}

	if u != nil || g != nil {
		crd = &syscall.Credential{}

		if u != nil {
			uid, err := strconv.ParseUint(u.Uid, 10, 32)
			if err != nil {
				return nil, err
			}

			crd.Uid = uint32(uid)
		}

		if g != nil {
			gid, err := strconv.ParseUint(g.Gid, 10, 32)
			if err != nil {
				return nil, err
			}

			crd.Gid = uint32(gid)
		}
	}

	return func() *exec.Cmd {
		cmd := exec.Command(cmd[0], cmd[1:]...)
		if crd != nil {
			cmd.SysProcAttr = &syscall.SysProcAttr{Credential: crd}
		}

		return cmd
	}, nil
}

// makeFactory creates and connects new factory instance based on given parameters.
func (cfg *ServerConfig) makeFactory() (Factory, error) {
	if cfg.Relay == "pipes" || cfg.Relay == "pipe" {
		return NewPipeFactory(), nil
	}

	dsn := strings.Split(cfg.Relay, "://")
	if len(dsn) != 2 {
		return nil, errors.New("invalid relay DSN (pipes, tcp://:6001, unix://rr.sock)")
	}

	ln, err := net.Listen(dsn[0], dsn[1])
	if err != nil {
		return nil, nil
	}

	return NewSocketFactory(ln, cfg.RelayTimeout), nil
}