diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 2791d36b..08c9295e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/opencontainers/runc", - "GoVersion": "go1.4.2", + "GoVersion": "go1.5.3", "Deps": [ { "ImportPath": "github.com/Sirupsen/logrus", @@ -12,6 +12,11 @@ "Comment": "1.2.0-187-gc31a797", "Rev": "c31a7975863e7810c92e2e288a9ab074f9a88f29" }, + { + "ImportPath": "github.com/coreos/go-systemd/activation", + "Comment": "v4", + "Rev": "b4a58d95188dd092ae20072bac14cece0e67c388" + }, { "ImportPath": "github.com/coreos/go-systemd/dbus", "Comment": "v4", diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files.go new file mode 100644 index 00000000..c8e85fcd --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files.go @@ -0,0 +1,52 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package activation implements primitives for systemd socket activation. +package activation + +import ( + "os" + "strconv" + "syscall" +) + +// based on: https://gist.github.com/alberts/4640792 +const ( + listenFdsStart = 3 +) + +func Files(unsetEnv bool) []*os.File { + if unsetEnv { + defer os.Unsetenv("LISTEN_PID") + defer os.Unsetenv("LISTEN_FDS") + } + + pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) + if err != nil || pid != os.Getpid() { + return nil + } + + nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) + if err != nil || nfds == 0 { + return nil + } + + files := make([]*os.File, 0, nfds) + for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { + syscall.CloseOnExec(fd) + files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd))) + } + + return files +} diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners.go new file mode 100644 index 00000000..df27c29e --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners.go @@ -0,0 +1,62 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package activation + +import ( + "crypto/tls" + "net" +) + +// Listeners returns a slice containing a net.Listener for each matching socket type +// passed to this process. +// +// The order of the file descriptors is preserved in the returned slice. +// Nil values are used to fill any gaps. For example if systemd were to return file descriptors +// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener} +func Listeners(unsetEnv bool) ([]net.Listener, error) { + files := Files(unsetEnv) + listeners := make([]net.Listener, len(files)) + + for i, f := range files { + if pc, err := net.FileListener(f); err == nil { + listeners[i] = pc + } + } + return listeners, nil +} + +// TLSListeners returns a slice containing a net.listener for each matching TCP socket type +// passed to this process. +// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig. +func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) { + listeners, err := Listeners(unsetEnv) + + if listeners == nil || err != nil { + return nil, err + } + + if tlsConfig != nil && err == nil { + tlsConfig.NextProtos = []string{"http/1.1"} + + for i, l := range listeners { + // Activate TLS only for TCP sockets + if l.Addr().Network() == "tcp" { + listeners[i] = tls.NewListener(l, tlsConfig) + } + } + } + + return listeners, err +} diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns.go new file mode 100644 index 00000000..48b2ca02 --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns.go @@ -0,0 +1,37 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package activation + +import ( + "net" +) + +// PacketConns returns a slice containing a net.PacketConn for each matching socket type +// passed to this process. +// +// The order of the file descriptors is preserved in the returned slice. +// Nil values are used to fill any gaps. For example if systemd were to return file descriptors +// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} +func PacketConns(unsetEnv bool) ([]net.PacketConn, error) { + files := Files(unsetEnv) + conns := make([]net.PacketConn, len(files)) + + for i, f := range files { + if pc, err := net.FilePacketConn(f); err == nil { + conns[i] = pc + } + } + return conns, nil +} diff --git a/start.go b/start.go index 55a65c1d..6fa53bb5 100644 --- a/start.go +++ b/start.go @@ -6,17 +6,15 @@ import ( "fmt" "os" "runtime" - "strconv" "syscall" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/coreos/go-systemd/activation" "github.com/opencontainers/runc/libcontainer" "github.com/opencontainers/specs" ) -const SD_LISTEN_FDS_START = 3 - // default action is to start a container var startCommand = cli.Command{ Name: "start", @@ -59,17 +57,10 @@ var startCommand = cli.Command{ setupSdNotify(spec, notifySocket) } - var ( - listenFds = os.Getenv("LISTEN_FDS") - listenPid = os.Getenv("LISTEN_PID") - ) - if listenFds != "" && listenPid == strconv.Itoa(os.Getpid()) { - setupSocketActivation(spec, listenFds) - } - if os.Geteuid() != 0 { logrus.Fatal("runc should be run as root") } + status, err := startContainer(context, spec) if err != nil { logrus.Fatalf("Container start failed: %v", err) @@ -124,12 +115,10 @@ func startContainer(context *cli.Context, spec *specs.LinuxSpec) (int, error) { process := newProcess(spec.Process) // Support on-demand socket activation by passing file descriptors into the container init process. if os.Getenv("LISTEN_FDS") != "" { - listenFdsInt, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) - if err != nil { - return -1, err - } - for i := SD_LISTEN_FDS_START; i < (listenFdsInt + SD_LISTEN_FDS_START); i++ { - process.ExtraFiles = append(process.ExtraFiles, os.NewFile(uintptr(i), "")) + listenFds := activation.Files(false) + if len(listenFds) > 0 { + process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(listenFds)), "LISTEN_PID=1") + process.ExtraFiles = append(process.ExtraFiles, listenFds...) } } tty, err := setupIO(process, rootuid, context.String("console"), spec.Process.Terminal, detach) diff --git a/utils.go b/utils.go index eda732ab..ed4d561b 100644 --- a/utils.go +++ b/utils.go @@ -236,12 +236,6 @@ func setupSdNotify(spec *specs.LinuxSpec, notifySocket string) { spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket)) } -// If systemd is supporting on-demand socket activation, this function will add support -// for on-demand socket activation for the containerized service. -func setupSocketActivation(spec *specs.LinuxSpec, listenFds string) { - spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("LISTEN_FDS=%s", listenFds), "LISTEN_PID=1") -} - func destroy(container libcontainer.Container) { if err := container.Destroy(); err != nil { logrus.Error(err)