Skip to content

Commit 9ee57ed

Browse files
committed
clair: add supervisor notification mechanism
This allows Clair to inform a process supervisor about its state. Currently, only systemd is supported and only "ready" and "stopping" messages are used. Signed-off-by: Hank Donnay <hdonnay@redhat.com>
1 parent 5caf1bb commit 9ee57ed

3 files changed

Lines changed: 127 additions & 0 deletions

File tree

cmd/clair/notify.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main
2+
3+
type notifyMsg uint
4+
5+
const (
6+
_ notifyMsg = iota
7+
msgReady
8+
msgReloading
9+
msgStopping
10+
msgStatus
11+
msgSocketAPI
12+
msgSocketIntrospection
13+
)

cmd/clair/notify_linux.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
"net"
8+
"os"
9+
"strings"
10+
11+
"golang.org/x/sys/unix"
12+
)
13+
14+
// Notify sends information back to a supervisor process.
15+
//
16+
// # Linux
17+
//
18+
// The supervisor is assumed to be systemd, and this function implements the
19+
// sd_notify(3) protocol. Some messages require an additional argument.
20+
func notify(args ...any) error {
21+
const key = `NOTIFY_SOCKET`
22+
23+
sockpath, ok := os.LookupEnv(key)
24+
if !ok {
25+
return nil
26+
}
27+
sock, err := net.ResolveUnixAddr("unix", sockpath)
28+
if err != nil {
29+
return err
30+
}
31+
conn, err := net.DialUnix("unix", nil, sock)
32+
if err != nil {
33+
return err
34+
}
35+
defer conn.Close()
36+
rc, err := conn.SyscallConn()
37+
if err != nil {
38+
return err
39+
}
40+
var ctlErr error
41+
err = rc.Control(func(fd uintptr) {
42+
const szTgt = 8 * 1026 * 1026 // 8 MiB
43+
44+
_, ctlErr = unix.FcntlInt(fd, unix.F_SETFD, unix.FD_CLOEXEC)
45+
if ctlErr != nil {
46+
return
47+
}
48+
49+
var sz int
50+
sz, ctlErr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF)
51+
if ctlErr != nil {
52+
return
53+
}
54+
if sz < szTgt {
55+
ctlErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, szTgt)
56+
if ctlErr != nil {
57+
return
58+
}
59+
}
60+
})
61+
if err := errors.Join(err, ctlErr); err != nil {
62+
return err
63+
}
64+
65+
// build message
66+
var buf bytes.Buffer
67+
var oob []byte
68+
for i := 0; i < len(args); i++ {
69+
var fdname string
70+
msg := args[i].(notifyMsg)
71+
switch msg {
72+
case msgReady:
73+
buf.WriteString("READY=1\n")
74+
case msgReloading:
75+
buf.WriteString("RELOADING=1\n")
76+
case msgStopping:
77+
buf.WriteString("STOPPING=1\n")
78+
case msgStatus:
79+
buf.WriteString("STATUS=")
80+
i++
81+
s := args[i].(string)
82+
buf.WriteString(strings.TrimSpace(s))
83+
buf.WriteByte('\n')
84+
case msgSocketAPI:
85+
fdname = "api"
86+
case msgSocketIntrospection:
87+
fdname = "introspection"
88+
default:
89+
panic(fmt.Sprintf("programmer error: unknown msg kind: %v", msg))
90+
}
91+
if fdname != "" {
92+
if oob != nil {
93+
panic("programmer error: sending multiple file descriptors")
94+
}
95+
buf.WriteString("FDSTORE=1\n")
96+
buf.WriteString("FDNAME=")
97+
buf.WriteString(fdname)
98+
buf.WriteByte('\n')
99+
i++
100+
oob = unix.UnixRights(args[i].(int))
101+
}
102+
}
103+
104+
_, _, err = conn.WriteMsgUnix(buf.Bytes(), oob, sock)
105+
return err
106+
}

cmd/clair/notify_other.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:build !linux
2+
3+
package main
4+
5+
// Notify sends information back to a supervisor process.
6+
func notify(_ ...any) error {
7+
return nil
8+
}

0 commit comments

Comments
 (0)