Skip to content

Commit 8052c95

Browse files
localai-botmudler
andauthored
fix(cli): ignore a half-populated socket activation environment (#11394)
A container engine started from a socket-activated system unit leaks a bare LISTEN_PID into every container it spawns, with no matching LISTEN_FDS. LocalAI read that as a malformed activation attempt and refused to start: ERROR Error running the application error=loading systemd socket activation listeners: invalid LISTEN_FDS "" systemd's own sd_listen_fds() treats either variable being absent as "not activated" rather than as an error, so do the same and fall back to ordinary --address binding. A value that is present but malformed is still rejected, so a real activation attempt cannot silently bind the wrong socket. Fixes #11390 Assisted-by: Claude:claude-opus-5 [golangci-lint] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 5ac445e commit 8052c95

3 files changed

Lines changed: 41 additions & 0 deletions

File tree

core/cli/run_socket_activation_linux.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ func systemdActivatedListeners() ([]net.Listener, error) {
2424
}
2525
}()
2626

27+
// A half-populated environment is not an activation attempt. Container runtimes
28+
// started from a socket-activated system unit leak a bare LISTEN_PID into every
29+
// container they spawn, and systemd's own sd_listen_fds() treats either variable
30+
// being absent as "not activated" rather than as an error.
31+
if listenPID == "" || listenFDs == "" {
32+
return nil, nil
33+
}
34+
2735
pid, err := strconv.Atoi(listenPID)
2836
if err != nil {
2937
return nil, fmt.Errorf("invalid LISTEN_PID %q: %w", listenPID, err)

core/cli/run_socket_activation_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,34 @@ var _ = Describe("systemdActivatedListeners", func() {
8585
Expect(os.Getenv("LISTEN_FDNAMES")).To(BeEmpty())
8686
})
8787

88+
It("binds normally when the environment leaks LISTEN_PID without LISTEN_FDS", func() {
89+
Expect(os.Setenv("LISTEN_PID", strconv.Itoa(os.Getpid()))).To(Succeed())
90+
Expect(os.Unsetenv("LISTEN_FDS")).To(Succeed())
91+
DeferCleanup(func() {
92+
_ = os.Unsetenv("LISTEN_PID")
93+
})
94+
95+
listeners, err := systemdActivatedListeners()
96+
97+
Expect(err).NotTo(HaveOccurred())
98+
Expect(listeners).To(BeEmpty())
99+
Expect(os.Getenv("LISTEN_PID")).To(BeEmpty())
100+
})
101+
102+
It("binds normally when the environment leaks LISTEN_FDS without LISTEN_PID", func() {
103+
Expect(os.Unsetenv("LISTEN_PID")).To(Succeed())
104+
Expect(os.Setenv("LISTEN_FDS", "1")).To(Succeed())
105+
DeferCleanup(func() {
106+
_ = os.Unsetenv("LISTEN_FDS")
107+
})
108+
109+
listeners, err := systemdActivatedListeners()
110+
111+
Expect(err).NotTo(HaveOccurred())
112+
Expect(listeners).To(BeEmpty())
113+
Expect(os.Getenv("LISTEN_FDS")).To(BeEmpty())
114+
})
115+
88116
It("reports malformed activation metadata instead of silently binding another socket", func() {
89117
Expect(os.Setenv("LISTEN_PID", strconv.Itoa(os.Getpid()))).To(Succeed())
90118
Expect(os.Setenv("LISTEN_FDS", "not-a-number")).To(Succeed())

docs/content/getting-started/linux.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ For a Podman-managed container, configure Podman to preserve and pass the
111111
systemd socket file descriptor into the container. The LocalAI process inside
112112
the container consumes the same activation protocol.
113113

114+
Activation needs both `LISTEN_PID` and `LISTEN_FDS`. If only one of them is set,
115+
LocalAI ignores them and binds `--address` as usual. A container engine started
116+
from a socket-activated system unit can leak a bare `LISTEN_PID` into every
117+
container it spawns, and that is not an activation attempt.
118+
114119
## Next Steps
115120

116121
- [Try it out with examples](/basics/try/)

0 commit comments

Comments
 (0)