Skip to content

Commit e15c4c4

Browse files
committed
activation: add two functions to provide listeners with names
Since v227, systemd can name each listener. This is useful when you have different socket units activating the same service: you can't really know in which order the socket will be provided. Systemd adds an environment variable named LISTEN_FDNAMES for this purpose. We encapsulate the name in the File object created from the file descriptor. Unfortunately, once this is turned into a net.Listener, there doesn't seem to have a way to fetch this information back. Therefore, two new functions are provided to be able to easily get named sockets. The same could be done to PacketConns in the future.
1 parent 749ef98 commit e15c4c4

File tree

8 files changed

+75
-22
lines changed

8 files changed

+75
-22
lines changed

activation/files.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package activation
1818
import (
1919
"os"
2020
"strconv"
21+
"strings"
2122
"syscall"
2223
)
2324

@@ -30,6 +31,7 @@ func Files(unsetEnv bool) []*os.File {
3031
if unsetEnv {
3132
defer os.Unsetenv("LISTEN_PID")
3233
defer os.Unsetenv("LISTEN_FDS")
34+
defer os.Unsetenv("LISTEN_FDNAMES")
3335
}
3436

3537
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
@@ -42,10 +44,17 @@ func Files(unsetEnv bool) []*os.File {
4244
return nil
4345
}
4446

47+
names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":")
48+
4549
files := make([]*os.File, 0, nfds)
4650
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
4751
syscall.CloseOnExec(fd)
48-
files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd)))
52+
name := "LISTEN_FD_" + strconv.Itoa(fd)
53+
offset := fd - listenFdsStart
54+
if offset < len(names) && len(names[offset]) > 0 {
55+
name = names[offset]
56+
}
57+
files = append(files, os.NewFile(uintptr(fd), name))
4958
}
5059

5160
return files

activation/files_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ func TestActivation(t *testing.T) {
4848
}
4949

5050
cmd.Env = os.Environ()
51-
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
51+
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1", "FIX_LISTEN_PID=1")
5252

5353
err := cmd.Run()
5454
if err != nil {
5555
t.Fatalf(err.Error())
5656
}
5757

58-
correctStringWritten(t, r1, "Hello world")
59-
correctStringWritten(t, r2, "Goodbye world")
58+
correctStringWritten(t, r1, "Hello world: fd1")
59+
correctStringWritten(t, r2, "Goodbye world: LISTEN_FD_4")
6060
}
6161

6262
func TestActivationNoFix(t *testing.T) {

activation/listeners.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,27 @@ func Listeners(unsetEnv bool) ([]net.Listener, error) {
3737
return listeners, nil
3838
}
3939

40+
// ListenersWithNames maps a listener name to a set of net.Listener instances.
41+
func ListenersWithNames(unsetEnv bool) (map[string][]net.Listener, error) {
42+
files := Files(unsetEnv)
43+
listeners := map[string][]net.Listener{}
44+
45+
for _, f := range files {
46+
if pc, err := net.FileListener(f); err == nil {
47+
current, ok := listeners[f.Name()]
48+
if !ok {
49+
listeners[f.Name()] = []net.Listener{pc}
50+
} else {
51+
listeners[f.Name()] = append(current, pc)
52+
}
53+
if unsetEnv {
54+
f.Close()
55+
}
56+
}
57+
}
58+
return listeners, nil
59+
}
60+
4061
// TLSListeners returns a slice containing a net.listener for each matching TCP socket type
4162
// passed to this process.
4263
// 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)
5879

5980
return listeners, err
6081
}
82+
83+
// TLSListenersWithNames maps a listener name to a net.Listener with
84+
// the associated TLS configuration.
85+
func TLSListenersWithNames(unsetEnv bool, tlsConfig *tls.Config) (map[string][]net.Listener, error) {
86+
listeners, err := ListenersWithNames(unsetEnv)
87+
88+
if listeners == nil || err != nil {
89+
return nil, err
90+
}
91+
92+
if tlsConfig != nil && err == nil {
93+
for _, ll := range listeners {
94+
// Activate TLS only for TCP sockets
95+
for i, l := range ll {
96+
if l.Addr().Network() == "tcp" {
97+
ll[i] = tls.NewListener(l, tlsConfig)
98+
}
99+
}
100+
}
101+
}
102+
103+
return listeners, err
104+
}

activation/listeners_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,14 @@ func TestListeners(t *testing.T) {
7373
r2.Write([]byte("Hi"))
7474

7575
cmd.Env = os.Environ()
76-
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
76+
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1:fd2", "FIX_LISTEN_PID=1")
7777

78-
out, err := cmd.Output()
78+
out, err := cmd.CombinedOutput()
7979
if err != nil {
8080
println(string(out))
8181
t.Fatalf(err.Error())
8282
}
8383

84-
correctStringWrittenNet(t, r1, "Hello world")
85-
correctStringWrittenNet(t, r2, "Goodbye world")
84+
correctStringWrittenNet(t, r1, "Hello world: fd1")
85+
correctStringWrittenNet(t, r2, "Goodbye world: fd2")
8686
}

activation/packetconns_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func TestPacketConns(t *testing.T) {
5656
r2.Write([]byte("Hi"))
5757

5858
cmd.Env = os.Environ()
59-
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
59+
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1:fd2", "FIX_LISTEN_PID=1")
6060

6161
out, err := cmd.CombinedOutput()
6262
if err != nil {

examples/activation/activation.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,19 @@ func main() {
4242
panic("No files")
4343
}
4444

45-
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
45+
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" || os.Getenv("LISTEN_FDNAMES") == "" {
4646
panic("Should not unset envs")
4747
}
4848

4949
files = activation.Files(true)
5050

51-
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
51+
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" {
5252
panic("Can not unset envs")
5353
}
5454

5555
// Write out the expected strings to the two pipes
56-
files[0].Write([]byte("Hello world"))
57-
files[1].Write([]byte("Goodbye world"))
56+
files[0].Write([]byte("Hello world: " + files[0].Name()))
57+
files[1].Write([]byte("Goodbye world: " + files[1].Name()))
5858

5959
return
6060
}

examples/activation/listen.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,25 @@ func main() {
4242
panic("No listeners")
4343
}
4444

45-
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
45+
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" || os.Getenv("LISTEN_FDNAMES") == "" {
4646
panic("Should not unset envs")
4747
}
4848

49-
listeners, err := activation.Listeners(true)
49+
listenersWithNames, err := activation.ListenersWithNames(true)
5050
if err != nil {
5151
panic(err)
5252
}
5353

54-
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
54+
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" {
5555
panic("Can not unset envs")
5656
}
5757

58-
c0, _ := listeners[0].Accept()
59-
c1, _ := listeners[1].Accept()
58+
c0, _ := listenersWithNames["fd1"][0].Accept()
59+
c1, _ := listenersWithNames["fd2"][0].Accept()
6060

6161
// Write out the expected strings to the two pipes
62-
c0.Write([]byte("Hello world"))
63-
c1.Write([]byte("Goodbye world"))
62+
c0.Write([]byte("Hello world: fd1"))
63+
c1.Write([]byte("Goodbye world: fd2"))
6464

6565
return
6666
}

examples/activation/udpconn.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func main() {
4343
panic("No packetConns")
4444
}
4545

46-
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
46+
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" || os.Getenv("LISTEN_FDNAMES") == "" {
4747
panic("Should not unset envs")
4848
}
4949

@@ -52,7 +52,7 @@ func main() {
5252
panic(err)
5353
}
5454

55-
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
55+
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" {
5656
panic("Can not unset envs")
5757
}
5858

0 commit comments

Comments
 (0)