Skip to content

Commit 6e7bcc6

Browse files
committed
fix: route desktop power actions through atom
1 parent 8f81375 commit 6e7bcc6

3 files changed

Lines changed: 75 additions & 8 deletions

File tree

internal/control/sdb.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,15 @@ func ListenSDB(path string, m *core.Manager, cfg SDBConfig) (*SDBServer, error)
7676
_ = os.Remove(path)
7777
return nil, fmt.Errorf("set sdb control mode: %w", err)
7878
}
79-
return &SDBServer{ln: ln, m: m, cfg: cfg}, nil
79+
return &SDBServer{ln: ln, m: m, cfg: cfg, shutdown: shutdownAction}, nil
8080
}
8181

82-
// SDBServer serves only status, enable and disable for sdbd.service.
82+
// SDBServer serves the fixed desktop operations delegated to ush-broker.
8383
type SDBServer struct {
84-
ln net.Listener
85-
m *core.Manager
86-
cfg SDBConfig
84+
ln net.Listener
85+
m *core.Manager
86+
cfg SDBConfig
87+
shutdown func(syscall.Signal) Reply
8788
}
8889

8990
func (s *SDBServer) Serve() {
@@ -119,6 +120,10 @@ func (s *SDBServer) handle(conn net.Conn) {
119120
_ = writeFrame(conn, s.setEnabled(true))
120121
case "sdb-disable":
121122
_ = writeFrame(conn, s.setEnabled(false))
123+
case "session-reboot":
124+
_ = writeFrame(conn, s.shutdown(syscall.SIGINT))
125+
case "session-poweroff":
126+
_ = writeFrame(conn, s.shutdown(syscall.SIGTERM))
122127
default:
123128
_ = writeFrame(conn, Reply{Error: "unknown command: " + req.Cmd})
124129
}

internal/control/sdb_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,65 @@ func TestSDBControlChecksPeerIdentity(t *testing.T) {
9494
t.Fatal("different inode accepted")
9595
}
9696
}
97+
98+
func TestSDBControlDelegatesOnlySessionPowerActions(t *testing.T) {
99+
if os.Geteuid() != 0 {
100+
t.Skip("the production socket owner check requires root")
101+
}
102+
exe, err := os.Executable()
103+
if err != nil {
104+
t.Fatal(err)
105+
}
106+
dir := t.TempDir()
107+
units := filepath.Join(dir, "units")
108+
if err := os.Mkdir(units, 0o755); err != nil {
109+
t.Fatal(err)
110+
}
111+
writeUnit(t, units, "base.target", "[Unit]\n")
112+
m, err := core.Build(&unit.Loader{Paths: []string{units}}, "base.target")
113+
if err != nil {
114+
t.Fatal(err)
115+
}
116+
117+
srv, err := ListenSDB(filepath.Join(dir, "sdb.sock"), m, SDBConfig{
118+
Unit: "sdbd.service",
119+
Marker: filepath.Join(dir, "state", "enabled"),
120+
BrokerExecutable: exe,
121+
GroupID: os.Getgid(),
122+
MinimumUID: 1000,
123+
})
124+
if err != nil {
125+
t.Fatal(err)
126+
}
127+
signals := make(chan syscall.Signal, 2)
128+
srv.shutdown = func(sig syscall.Signal) Reply {
129+
signals <- sig
130+
return Reply{OK: true}
131+
}
132+
go srv.Serve()
133+
defer srv.Close()
134+
135+
for _, tc := range []struct {
136+
cmd string
137+
want syscall.Signal
138+
}{
139+
{cmd: "session-reboot", want: syscall.SIGINT},
140+
{cmd: "session-poweroff", want: syscall.SIGTERM},
141+
} {
142+
rep, err := Send(filepath.Join(dir, "sdb.sock"), Request{Cmd: tc.cmd})
143+
if err != nil || !rep.OK {
144+
t.Fatalf("%s: err=%v rep=%+v", tc.cmd, err, rep)
145+
}
146+
if got := <-signals; got != tc.want {
147+
t.Fatalf("%s signal=%v, want %v", tc.cmd, got, tc.want)
148+
}
149+
}
150+
151+
rep, err := Send(filepath.Join(dir, "sdb.sock"), Request{Cmd: "restart", Unit: "other.service"})
152+
if err != nil {
153+
t.Fatal(err)
154+
}
155+
if rep.OK || rep.Error == "" {
156+
t.Fatalf("arbitrary init command accepted: %+v", rep)
157+
}
158+
}

internal/control/server.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ func (s *Server) mutate(req Request) Reply {
111111
// Accepted for wire compatibility; unit reload and boot confirmation
112112
// are not handled by the init.
113113
case "reboot":
114-
return s.shutdownAction(syscall.SIGINT) // waitForShutdown maps SIGINT -> reboot
114+
return shutdownAction(syscall.SIGINT) // waitForShutdown maps SIGINT -> reboot
115115
case "poweroff", "halt":
116-
return s.shutdownAction(syscall.SIGTERM)
116+
return shutdownAction(syscall.SIGTERM)
117117
}
118118
return Reply{OK: true, State: s.m.State(req.Unit)}
119119
}
@@ -122,7 +122,7 @@ func (s *Server) mutate(req Request) Reply {
122122
// It signals the init (self) so the single tested shutdown path in waitForShutdown
123123
// runs; it refuses when not running as the init, so a dev/test invocation cannot
124124
// signal the host's real PID 1.
125-
func (s *Server) shutdownAction(sig syscall.Signal) Reply {
125+
func shutdownAction(sig syscall.Signal) Reply {
126126
if os.Getpid() != 1 {
127127
return Reply{Error: "reboot/poweroff is only available to the init (pid 1)"}
128128
}

0 commit comments

Comments
 (0)