Skip to content

Commit 2997cf6

Browse files
committed
fix: route debug bridge control through atom
1 parent bb712b7 commit 2997cf6

4 files changed

Lines changed: 70 additions & 329 deletions

File tree

cmd/ush-broker/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ func main() {
2626
}
2727

2828
func run() error {
29+
if _, _, errno := syscall.Syscall6(syscall.SYS_PRCTL, 4, 0, 0, 0, 0, 0); errno != 0 {
30+
return fmt.Errorf("disable process dumpability: %w", errno)
31+
}
32+
2933
cfg, err := config.Load()
3034
if err != nil {
3135
return fmt.Errorf("config: %w", err)

internal/broker/atomctl.go

Lines changed: 27 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import (
2121
"io"
2222
"net"
2323
"os"
24-
"path/filepath"
2524
"time"
2625
)
2726

2827
const defaultAtomControlSocket = "/run/atom/control.sock"
28+
const defaultAtomSDBControlSocket = "/run/atom/sdb-control.sock"
2929

3030
const (
3131
atomDialTimeout = 3 * time.Second
@@ -40,52 +40,14 @@ func atomControlSocketPath() string {
4040
return defaultAtomControlSocket
4141
}
4242

43-
// defaultSdbOptIn is the per-bridge marker sdbd and its unit both require. It is
44-
// deliberately NOT the development marker: that one governs the whole image and
45-
// lives on the read-only verity tree, while this one governs the bridge alone
46-
// and lives on writable storage. Absent means off, so a fresh image carries no
47-
// listener until the owner asks for one.
48-
const defaultSdbOptIn = "/var/lib/sinty-sdb/enabled"
49-
50-
func sdbOptInPath() string {
51-
if p := os.Getenv("USH_SDB_OPT_IN"); p != "" {
43+
func atomSDBControlSocketPath() string {
44+
if p := os.Getenv("USH_ATOM_SDB_CONTROL_SOCK"); p != "" {
5245
return p
5346
}
54-
return defaultSdbOptIn
55-
}
56-
57-
// setSdbOptIn creates or removes the marker. Removal treats an already-absent
58-
// marker as done, so turning off a bridge that is already off is not an error.
59-
func setSdbOptIn(on bool) error {
60-
path := sdbOptInPath()
61-
if !on {
62-
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
63-
return fmt.Errorf("sdb opt-in: remove %s: %w", path, err)
64-
}
65-
return nil
66-
}
67-
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
68-
return fmt.Errorf("sdb opt-in: create dir: %w", err)
69-
}
70-
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
71-
if err != nil {
72-
return fmt.Errorf("sdb opt-in: create %s: %w", path, err)
47+
if p := os.Getenv("USH_ATOM_CONTROL_SOCK"); p != "" {
48+
return p
7349
}
74-
return f.Close()
75-
}
76-
77-
// sdbUnitName is the unit the debug bridge runs as. It is overridable because
78-
// the unit is shipped by the image rather than by this repository; if the name
79-
// is wrong, Present reports false and the desktop greys the control out, which
80-
// is the safe direction.
81-
func sdbUnitName() string {
82-
if n := os.Getenv("USH_SDB_UNIT"); n != "" {
83-
return n
84-
}
85-
// The image ships the bridge as sdbd.service (package sinty-sdb installs
86-
// dist/sdbd.service). sinit reports and keys units by their full file name,
87-
// so the broker must match that exactly, including the .service suffix.
88-
return "sdbd.service"
50+
return defaultAtomSDBControlSocket
8951
}
9052

9153
type atomRequest struct {
@@ -141,10 +103,10 @@ func atomReadFrame(r io.Reader, v any) error {
141103

142104
// atomControlCall sends one request to PID 1 and returns the reply. Every
143105
// failure is an error; a reply that merely arrived is not a success.
144-
func atomControlCall(req atomRequest) (atomReply, error) {
145-
conn, err := net.DialTimeout("unix", atomControlSocketPath(), atomDialTimeout)
106+
func atomControlCallAt(path string, req atomRequest) (atomReply, error) {
107+
conn, err := net.DialTimeout("unix", path, atomDialTimeout)
146108
if err != nil {
147-
return atomReply{}, fmt.Errorf("init control unreachable: %w", err)
109+
return atomReply{}, fmt.Errorf("init control unreachable at %s: %w", path, err)
148110
}
149111
defer conn.Close()
150112

@@ -159,55 +121,38 @@ func atomControlCall(req atomRequest) (atomReply, error) {
159121
return rep, nil
160122
}
161123

124+
func atomControlCall(req atomRequest) (atomReply, error) {
125+
return atomControlCallAt(atomControlSocketPath(), req)
126+
}
127+
128+
func atomSDBControlCall(req atomRequest) (atomReply, error) {
129+
return atomControlCallAt(atomSDBControlSocketPath(), req)
130+
}
131+
162132
// atomSdbService controls the debug bridge unit through the init.
163133
type atomSdbService struct{}
164134

165-
// Present reports whether the unit is known to the init. list-units needs no
166-
// privilege, so this answers even where a mutation would be refused.
135+
// Present reports whether the unit file is known to the init. status also sees
136+
// units outside the boot graph without starting them.
167137
func (atomSdbService) Present() (bool, error) {
168-
rep, err := atomControlCall(atomRequest{Cmd: "list-units"})
138+
rep, err := atomSDBControlCall(atomRequest{Cmd: "sdb-status"})
169139
if err != nil {
170140
return false, err
171141
}
172142
if !rep.OK {
173-
return false, fmt.Errorf("init control: list-units: %s", atomError(rep))
174-
}
175-
want := sdbUnitName()
176-
for _, u := range rep.Units {
177-
if u.Name == want {
178-
return true, nil
179-
}
143+
return false, fmt.Errorf("init control: status: %s", atomError(rep))
180144
}
181-
return false, nil
145+
return rep.State != "unknown" && rep.State != "not-found", nil
182146
}
183147

184-
// SetEnabled switches the bridge in both halves: the persistent marker, then the
185-
// live state. It deliberately does not attempt to change unit enablement, which
186-
// does not exist as a live operation here (see the file comment).
187-
//
188-
// The marker always moves FIRST, in both directions, and the init command is
189-
// issued only if that succeeded. The ordering is the safety property: a crash
190-
// between the two steps must leave the closed state, never the open one.
191-
//
192-
// - disabling removes the marker before the stop, so an interrupted disable
193-
// leaves a bridge that is not allowed to come back at next boot
194-
// - enabling creates it before the start, so an interrupted enable leaves a
195-
// bridge that is allowed but not running, which the next status read reports
196-
// honestly as inactive
197-
//
198-
// A marker that cannot be written is a hard failure and no init command is sent:
199-
// changing the live state while the persistent state stayed behind is exactly
200-
// the disagreement this ordering exists to prevent.
148+
// SetEnabled asks PID 1 to update both the persistent gate and live state. The
149+
// dedicated socket accepts no arbitrary unit name or general init operation.
201150
func (atomSdbService) SetEnabled(on bool) error {
202-
if err := setSdbOptIn(on); err != nil {
203-
return err
204-
}
205-
206-
cmd := "stop"
151+
cmd := "sdb-disable"
207152
if on {
208-
cmd = "start"
153+
cmd = "sdb-enable"
209154
}
210-
rep, err := atomControlCall(atomRequest{Cmd: cmd, Unit: sdbUnitName()})
155+
rep, err := atomSDBControlCall(atomRequest{Cmd: cmd})
211156
if err != nil {
212157
return err
213158
}

0 commit comments

Comments
 (0)