diff --git a/command/agent/command.go b/command/agent/command.go
index 0993ccfa15bf..5b3c08a394d3 100644
--- a/command/agent/command.go
+++ b/command/agent/command.go
@@ -18,6 +18,7 @@ import (
"github.com/armon/go-metrics/datadog"
"github.com/hashicorp/consul/watch"
"github.com/hashicorp/go-checkpoint"
+ "github.com/hashicorp/go-reap"
"github.com/hashicorp/go-syslog"
"github.com/hashicorp/logutils"
scada "github.com/hashicorp/scada-client"
@@ -641,6 +642,33 @@ func (c *Command) Run(args []string) int {
defer server.Shutdown()
}
+ // Enable child process reaping
+ if (config.Reap != nil && *config.Reap) || (config.Reap == nil && os.Getpid() == 1) {
+ if !reap.IsSupported() {
+ c.Ui.Error("Child process reaping is not supported on this platform (set reap=false)")
+ return 1
+ } else {
+ logger := c.agent.logger
+ logger.Printf("[DEBUG] Automatically reaping child processes")
+
+ pids := make(reap.PidCh, 1)
+ errors := make(reap.ErrorCh, 1)
+ go func() {
+ for {
+ select {
+ case pid := <-pids:
+ logger.Printf("[DEBUG] Reaped child process %d", pid)
+ case err := <-errors:
+ logger.Printf("[ERR] Error reaping child process: %v", err)
+ case <-c.agent.shutdownCh:
+ return
+ }
+ }
+ }()
+ go reap.ReapChildren(pids, errors, c.agent.shutdownCh)
+ }
+ }
+
// Check and shut down the SCADA listeners at the end
defer func() {
if c.scadaHttp != nil {
diff --git a/command/agent/config.go b/command/agent/config.go
index c03659116f26..d0c68df4c455 100644
--- a/command/agent/config.go
+++ b/command/agent/config.go
@@ -422,6 +422,17 @@ type Config struct {
// Minimum Session TTL
SessionTTLMin time.Duration `mapstructure:"-"`
SessionTTLMinRaw string `mapstructure:"session_ttl_min"`
+
+ // Reap controls automatic reaping of child processes, useful if running
+ // as PID 1 in a Docker container. This defaults to nil which will make
+ // Consul reap only if it detects it's running as PID 1. If non-nil,
+ // then this will be used to decide if reaping is enabled.
+ Reap *bool `mapstructure:"reap"`
+}
+
+// Bool is used to initialize bool pointers in struct literals.
+func Bool(b bool) *bool {
+ return &b
}
// UnixSocketPermissions contains information about a unix socket, and
@@ -1140,6 +1151,10 @@ func MergeConfig(a, b *Config) *Config {
result.RetryJoinWan = append(result.RetryJoinWan, a.RetryJoinWan...)
result.RetryJoinWan = append(result.RetryJoinWan, b.RetryJoinWan...)
+ if b.Reap != nil {
+ result.Reap = b.Reap
+ }
+
return &result
}
diff --git a/command/agent/config_test.go b/command/agent/config_test.go
index 000567b26554..72af551ab2da 100644
--- a/command/agent/config_test.go
+++ b/command/agent/config_test.go
@@ -777,6 +777,27 @@ func TestDecodeConfig(t *testing.T) {
if config.SessionTTLMin != 5*time.Second {
t.Fatalf("bad: %s %#v", config.SessionTTLMin.String(), config)
}
+
+ // Reap
+ input = `{"reap": true}`
+ config, err = DecodeConfig(bytes.NewReader([]byte(input)))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if config.Reap == nil || *config.Reap != true {
+ t.Fatalf("bad: reap not enabled: %#v", config)
+ }
+
+ input = `{}`
+ config, err = DecodeConfig(bytes.NewReader([]byte(input)))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if config.Reap != nil {
+ t.Fatalf("bad: reap not tri-stated: %#v", config)
+ }
}
func TestDecodeConfig_invalidKeys(t *testing.T) {
@@ -1266,6 +1287,7 @@ func TestMergeConfig(t *testing.T) {
RPC: &net.TCPAddr{},
RPCRaw: "127.0.0.5:1233",
},
+ Reap: Bool(true),
}
c := MergeConfig(a, b)
diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown
index cadd50621bd2..049d4346350a 100644
--- a/website/source/docs/agent/options.html.markdown
+++ b/website/source/docs/agent/options.html.markdown
@@ -504,6 +504,11 @@ definitions support being updated during a reload.
* `protocol` Equivalent to the
[`-protocol` command-line flag](#_protocol).
+* `reap` controls Consul's automatic reaping of child processes, which
+ is useful if Consul is running as PID 1 in a Docker container. If this isn't specified, then Consul will
+ automatically reap child processes if it detects it is running as PID 1. If this is specified, then it
+ controls reaping regardless of Consul's PID.
+
* `recursor` Provides a single recursor address.
This has been deprecated, and the value is appended to the [`recursors`](#recursors) list for
backwards compatibility.