author | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-05-28 21:14:21 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-05-28 23:27:27 UTC |
parent | 69739305e20c05fe85f223d927d801914435c4fa |
systemd.go | +32 | -10 |
systemd_test.go | +43 | -2 |
diff --git a/systemd.go b/systemd.go index 4ba1ed4..159b528 100644 --- a/systemd.go +++ b/systemd.go @@ -27,6 +27,7 @@ var ( var files map[string][]*os.File var listeners map[string][]net.Listener var parseError error +var listenError error var mutex sync.Mutex // parse files, updating the global state. @@ -69,13 +70,19 @@ func parse() { return } - // We should have as many names as we have descriptors. - // Note that if we have no descriptors, fdNames will be [""] (due to how - // strings.Split works), so we consider that special case. - if nfds > 0 && (fdNamesStr == "" || len(fdNames) != nfds) { - parseError = fmt.Errorf( - "Incorrect LISTEN_FDNAMES, have you set FileDescriptorName?") - return + // If LISTEN_FDNAMES is set at all, it should have as many names as we + // have descriptors. If it isn't set, then we map them all to "". + if fdNamesStr == "" { + fdNames = []string{} + for i := 0; i < nfds; i++ { + fdNames = append(fdNames, "") + } + } else { + if nfds > 0 && len(fdNames) != nfds { + parseError = fmt.Errorf( + "Incorrect LISTEN_FDNAMES, have you set FileDescriptorName?") + return + } } files = map[string][]*os.File{} @@ -92,12 +99,15 @@ func parse() { f := os.NewFile(uintptr(fd), sysName) files[name] = append(files[name], f) + // Note this can fail for non-TCP listeners, so we put the error in a + // separate variable. lis, err := net.FileListener(f) if err != nil { - parseError = fmt.Errorf( + listenError = fmt.Errorf( "Error making listener out of fd %d: %v", fd, err) + } else { + listeners[name] = append(listeners[name], lis) } - listeners[name] = append(listeners[name], lis) } // Remove them from the environment, to prevent accidental reuse (by @@ -116,6 +126,9 @@ func parse() { // systemd socket unit. Multiple socket units can have the same name, hence // the slice of listeners for each name. // +// If the "FileDescriptorName=" option is not used, then all file descriptors +// are mapped to the "" name. +// // Ideally you should not need to call this more than once. If you do, the // same listeners will be returned, as repeated calls to this function will // return the same results: the parsing is done only once, and the results are @@ -125,7 +138,10 @@ func parse() { // how the passing works. func Listeners() (map[string][]net.Listener, error) { parse() - return listeners, parseError + if parseError != nil { + return listeners, parseError + } + return listeners, listenError } // OneListener returns a net.Listener for the first systemd socket with the @@ -142,6 +158,9 @@ func OneListener(name string) (net.Listener, error) { if parseError != nil { return nil, parseError } + if listenError != nil { + return nil, listenError + } lis := listeners[name] if len(lis) < 1 { @@ -185,6 +204,9 @@ func Listen(netw, laddr string) (net.Listener, error) { // systemd socket unit. Multiple socket units can have the same name, hence // the slice of listeners for each name. // +// If the "FileDescriptorName=" option is not used, then all file descriptors +// are mapped to the "" name. +// // Ideally you should not need to call this more than once. If you do, the // same files will be returned, as repeated calls to this function will return // the same results: the parsing is done only once, and the results are saved diff --git a/systemd_test.go b/systemd_test.go index e005b67..5e7686d 100644 --- a/systemd_test.go +++ b/systemd_test.go @@ -17,6 +17,7 @@ func setenv(pid, fds string, names ...string) { files = nil listeners = nil parseError = nil + listenError = nil } func TestEmptyEnvironment(t *testing.T) { @@ -54,7 +55,6 @@ func TestBadEnvironment(t *testing.T) { {ourPID, "a", []string{"name"}}, // Invalid number of fds. {"1", "1", []string{"name"}}, // PID != ourselves. {ourPID, "1", []string{"name1", "name2"}}, // Too many names. - {ourPID, "1", []string{}}, // Not enough names. } for _, c := range cases { setenv(c.pid, c.fds, c.names...) @@ -122,9 +122,27 @@ func TestBadFDs(t *testing.T) { setenv(strconv.Itoa(os.Getpid()), "1") firstFD = int(f.Fd()) - if ls, err := Listeners(); len(ls) != 1 || err == nil { + + if ls, err := Listeners(); len(ls) != 0 || err == nil { t.Errorf("Got a non-empty result: %v // %v", ls, err) } + + if l, err := OneListener(""); l != nil || err == nil { + t.Errorf("Got a non-empty result: %v // %v", l, err) + } + + // It's not a bad FD as far as Files() is concerned. + fs, err := Files() + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if len(fs) != 1 || len(fs[""]) != 1 { + t.Errorf("Unexpected result: %v", fs) + } + if got := fs[""][0]; got.Fd() != f.Fd() { + t.Errorf("File descriptor %d != expected %d (%v)", + got.Fd(), f.Fd(), got) + } } // newListener creates a TCP listener. @@ -284,6 +302,29 @@ func TestManySockets(t *testing.T) { os.Getenv("LISTEN_FDNAMES") != "" { t.Errorf("Failed to reset the environment") } + + // Test that things also work with LISTEN_FDNAMES unset. + setenv(strconv.Itoa(os.Getpid()), "2") + os.Unsetenv("LISTEN_FDNAMES") + { + lsMap, err := Listeners() + if err != nil || len(lsMap) != 1 || len(lsMap[""]) != 2 { + t.Fatalf("Got an invalid result: %v // %v", lsMap, err) + } + + ls := []net.Listener{ + lsMap[""][0], + lsMap[""][1], + } + + for i := 0; i < 2; i++ { + if !sameAddr(ls[i].Addr(), expected[i].Addr()) { + t.Errorf("Listener %d address mismatch, expected %#v, got %#v", + i, ls[i].Addr(), expected[i].Addr()) + } + } + } + } func TestListen(t *testing.T) {