diff --git a/hack/integration-systemd-socket.sh b/hack/integration-systemd-socket.sh new file mode 100755 index 00000000..1129a729 --- /dev/null +++ b/hack/integration-systemd-socket.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -e +if [ -z "$EXECED" ] +then + systemd-socket-activate -E EXECED=1 -l /tmp/activate.sock socat ACCEPT-FD:3 EXEC:"rootlesskit $0",nofork 2>/dev/null & + OUTPUT="$(curl --unix-socket /tmp/activate.sock http://localhost/hello 2>/dev/null)" + [ "$(printf 'Hello\n' )" = "$OUTPUT" ] || exit 1 +else + [ "$LISTEN_FDS" = "1" ] || exit 1 + read -r REQUEST + if [ "$(printf 'GET /hello HTTP/1.1\r\n')" = "$REQUEST" ] + then + printf 'HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nHello\n' + else + printf 'HTTP/1.1 400 Bad Request\r\nContent-Length: 5\r\n\r\nBad!\n' + fi +fi diff --git a/pkg/child/child.go b/pkg/child/child.go index 04846f5c..f76d2837 100644 --- a/pkg/child/child.go +++ b/pkg/child/child.go @@ -34,6 +34,23 @@ var propagationStates = map[string]uintptr{ "rslave": uintptr(unix.MS_REC | unix.MS_SLAVE), } +func setupFiles(cmd *exec.Cmd) { + // 0 1 and 2 are used for stdin. stdout, and stderr + const firstExtraFD = 3 + systemdActivationFDs := 0 + // check for systemd socket activation sockets + if v := os.Getenv("LISTEN_FDS"); v != "" { + if num, err := strconv.Atoi(v); err == nil { + systemdActivationFDs = num + cmd.ExtraFiles = make([]*os.File, systemdActivationFDs) + } + } + for fd := 0; fd < systemdActivationFDs; fd++ { + cmd.ExtraFiles[fd] = os.NewFile(uintptr(firstExtraFD + fd), "") + } +} + + func createCmd(targetCmd []string) (*exec.Cmd, error) { var args []string if len(targetCmd) > 1 { @@ -47,6 +64,7 @@ func createCmd(targetCmd []string) (*exec.Cmd, error) { cmd.SysProcAttr = &syscall.SysProcAttr{ Pdeathsig: syscall.SIGKILL, } + setupFiles(cmd) return cmd, nil } diff --git a/pkg/parent/parent.go b/pkg/parent/parent.go index 3d559e87..9cf607d4 100644 --- a/pkg/parent/parent.go +++ b/pkg/parent/parent.go @@ -125,6 +125,27 @@ func LockStateDir(stateDir string) (*flock.Flock, error) { return lock, nil } +func setupFilesAndEnv(cmd *exec.Cmd, readPipe *os.File, writePipe *os.File, envKey string) { + // 0 1 and 2 are used for stdin. stdout, and stderr + const firstExtraFD = 3 + systemdActivationFDs := 0 + // check for systemd socket activation sockets + if v := os.Getenv("LISTEN_FDS"); v != "" { + if num, err := strconv.Atoi(v); err == nil { + systemdActivationFDs = num + } + } + cmd.ExtraFiles = make([]*os.File, systemdActivationFDs + 2) + for fd := 0; fd < systemdActivationFDs; fd++ { + cmd.ExtraFiles[fd] = os.NewFile(uintptr(firstExtraFD + fd), "") + } + readIndex := systemdActivationFDs + writeIndex := readIndex + 1 + cmd.ExtraFiles[readIndex] = readPipe + cmd.ExtraFiles[writeIndex] = writePipe + cmd.Env = append(os.Environ(), envKey+"="+strconv.Itoa(firstExtraFD+readIndex)+","+strconv.Itoa(firstExtraFD+writeIndex)) +} + func Parent(opt Opt) error { if err := checkPreflight(opt); err != nil { return err @@ -178,8 +199,7 @@ func Parent(opt Opt) error { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - cmd.ExtraFiles = []*os.File{pipeR, pipe2W} - cmd.Env = append(os.Environ(), opt.PipeFDEnvKey+"=3,4") + setupFilesAndEnv(cmd, pipeR, pipe2W, opt.PipeFDEnvKey) if opt.StateDirEnvKey != "" { cmd.Env = append(cmd.Env, opt.StateDirEnvKey+"="+opt.StateDir) }