diff --git a/activation/files.go b/activation/files.go index c8e85fcd..4e067290 100644 --- a/activation/files.go +++ b/activation/files.go @@ -18,6 +18,7 @@ package activation import ( "os" "strconv" + "strings" "syscall" ) @@ -30,6 +31,7 @@ func Files(unsetEnv bool) []*os.File { if unsetEnv { defer os.Unsetenv("LISTEN_PID") defer os.Unsetenv("LISTEN_FDS") + defer os.Unsetenv("LISTEN_FDNAMES") } pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) @@ -42,10 +44,17 @@ func Files(unsetEnv bool) []*os.File { return nil } + names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") + 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))) + name := "LISTEN_FD_" + strconv.Itoa(fd) + offset := fd - listenFdsStart + if offset < len(names) && len(names[offset]) > 0 { + name = names[offset] + } + files = append(files, os.NewFile(uintptr(fd), name)) } return files diff --git a/activation/files_test.go b/activation/files_test.go index 8e15f2a1..a3da5e91 100644 --- a/activation/files_test.go +++ b/activation/files_test.go @@ -48,15 +48,15 @@ func TestActivation(t *testing.T) { } cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1") + cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1", "FIX_LISTEN_PID=1") err := cmd.Run() if err != nil { t.Fatalf(err.Error()) } - correctStringWritten(t, r1, "Hello world") - correctStringWritten(t, r2, "Goodbye world") + correctStringWritten(t, r1, "Hello world: fd1") + correctStringWritten(t, r2, "Goodbye world: LISTEN_FD_4") } func TestActivationNoFix(t *testing.T) { diff --git a/activation/listeners.go b/activation/listeners.go index fd5dfc70..c21d1ed9 100644 --- a/activation/listeners.go +++ b/activation/listeners.go @@ -37,6 +37,27 @@ func Listeners(unsetEnv bool) ([]net.Listener, error) { return listeners, nil } +// ListenersWithNames maps a listener name to a set of net.Listener instances. +func ListenersWithNames(unsetEnv bool) (map[string][]net.Listener, error) { + files := Files(unsetEnv) + listeners := map[string][]net.Listener{} + + for _, f := range files { + if pc, err := net.FileListener(f); err == nil { + current, ok := listeners[f.Name()] + if !ok { + listeners[f.Name()] = []net.Listener{pc} + } else { + listeners[f.Name()] = append(current, pc) + } + if unsetEnv { + f.Close() + } + } + } + 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. @@ -58,3 +79,26 @@ func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) return listeners, err } + +// TLSListenersWithNames maps a listener name to a net.Listener with +// the associated TLS configuration. +func TLSListenersWithNames(unsetEnv bool, tlsConfig *tls.Config) (map[string][]net.Listener, error) { + listeners, err := ListenersWithNames(unsetEnv) + + if listeners == nil || err != nil { + return nil, err + } + + if tlsConfig != nil && err == nil { + for _, ll := range listeners { + // Activate TLS only for TCP sockets + for i, l := range ll { + if l.Addr().Network() == "tcp" { + ll[i] = tls.NewListener(l, tlsConfig) + } + } + } + } + + return listeners, err +} diff --git a/activation/listeners_test.go b/activation/listeners_test.go index 72fb0ff6..333587c9 100644 --- a/activation/listeners_test.go +++ b/activation/listeners_test.go @@ -73,14 +73,14 @@ func TestListeners(t *testing.T) { r2.Write([]byte("Hi")) cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1") + cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1:fd2", "FIX_LISTEN_PID=1") - out, err := cmd.Output() + out, err := cmd.CombinedOutput() if err != nil { println(string(out)) t.Fatalf(err.Error()) } - correctStringWrittenNet(t, r1, "Hello world") - correctStringWrittenNet(t, r2, "Goodbye world") + correctStringWrittenNet(t, r1, "Hello world: fd1") + correctStringWrittenNet(t, r2, "Goodbye world: fd2") } diff --git a/activation/packetconns_test.go b/activation/packetconns_test.go index 8449756c..6b102857 100644 --- a/activation/packetconns_test.go +++ b/activation/packetconns_test.go @@ -56,7 +56,7 @@ func TestPacketConns(t *testing.T) { r2.Write([]byte("Hi")) cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1") + cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1:fd2", "FIX_LISTEN_PID=1") out, err := cmd.CombinedOutput() if err != nil { diff --git a/examples/activation/activation.go b/examples/activation/activation.go index d365d27b..a1e6fa33 100644 --- a/examples/activation/activation.go +++ b/examples/activation/activation.go @@ -42,19 +42,19 @@ func main() { panic("No files") } - if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" { + if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" || os.Getenv("LISTEN_FDNAMES") == "" { panic("Should not unset envs") } files = activation.Files(true) - if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" { + if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" { panic("Can not unset envs") } // Write out the expected strings to the two pipes - files[0].Write([]byte("Hello world")) - files[1].Write([]byte("Goodbye world")) + files[0].Write([]byte("Hello world: " + files[0].Name())) + files[1].Write([]byte("Goodbye world: " + files[1].Name())) return } diff --git a/examples/activation/listen.go b/examples/activation/listen.go index 062db156..270ec32e 100644 --- a/examples/activation/listen.go +++ b/examples/activation/listen.go @@ -42,25 +42,25 @@ func main() { panic("No listeners") } - if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" { + if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" || os.Getenv("LISTEN_FDNAMES") == "" { panic("Should not unset envs") } - listeners, err := activation.Listeners(true) + listenersWithNames, err := activation.ListenersWithNames(true) if err != nil { panic(err) } - if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" { + if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" { panic("Can not unset envs") } - c0, _ := listeners[0].Accept() - c1, _ := listeners[1].Accept() + c0, _ := listenersWithNames["fd1"][0].Accept() + c1, _ := listenersWithNames["fd2"][0].Accept() // Write out the expected strings to the two pipes - c0.Write([]byte("Hello world")) - c1.Write([]byte("Goodbye world")) + c0.Write([]byte("Hello world: fd1")) + c1.Write([]byte("Goodbye world: fd2")) return } diff --git a/examples/activation/udpconn.go b/examples/activation/udpconn.go index f65194d4..2d8d6058 100644 --- a/examples/activation/udpconn.go +++ b/examples/activation/udpconn.go @@ -43,7 +43,7 @@ func main() { panic("No packetConns") } - if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" { + if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" || os.Getenv("LISTEN_FDNAMES") == "" { panic("Should not unset envs") } @@ -52,7 +52,7 @@ func main() { panic(err) } - if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" { + if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" { panic("Can not unset envs") }