Skip to content

Commit ec57f31

Browse files
mapleafgoclaude
andcommitted
fix(ipc): Windows 服务已存在时更新配置,添加 DACL 权限
- InstallService 在服务已存在时更新配置和 DACL 而非报错 - 启动类型从 Automatic 改为 Manual - 添加 setServiceDACL 授权 Authenticated Users 启停查询服务 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent beb2e5b commit ec57f31

1 file changed

Lines changed: 51 additions & 4 deletions

File tree

ipc/service_windows.go

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ package ipc
55
import (
66
"fmt"
77
"os"
8+
"unsafe"
89

10+
"golang.org/x/sys/windows"
911
"golang.org/x/sys/windows/svc/mgr"
1012
)
1113

@@ -30,20 +32,30 @@ func InstallService() error {
3032

3133
s, err := m.OpenService(ServiceName)
3234
if err == nil {
33-
s.Close()
34-
return fmt.Errorf("service %s already exists", ServiceName)
35+
// Service already exists — update config and DACL to latest.
36+
defer s.Close()
37+
if err := s.UpdateConfig(mgr.Config{
38+
DisplayName: "Singcast Service",
39+
Description: "Singcast proxy core service",
40+
StartType: mgr.StartManual,
41+
BinaryPathName: fmt.Sprintf(`"%s" ipc --home "%s"`, exe, homeDir),
42+
}); err != nil {
43+
return fmt.Errorf("update service config: %w", err)
44+
}
45+
return setServiceDACL(ServiceName)
3546
}
3647

3748
s, err = m.CreateService(ServiceName, exe, mgr.Config{
3849
DisplayName: "Singcast Service",
3950
Description: "Singcast proxy core service",
40-
StartType: mgr.StartAutomatic,
51+
StartType: mgr.StartManual,
4152
}, "ipc", "--home", homeDir)
4253
if err != nil {
4354
return fmt.Errorf("create service: %w", err)
4455
}
4556
s.Close()
46-
return nil
57+
58+
return setServiceDACL(ServiceName)
4759
}
4860

4961
func UninstallService() error {
@@ -64,3 +76,38 @@ func UninstallService() error {
6476
}
6577
return nil
6678
}
79+
80+
// setServiceDACL grants Authenticated Users the rights to start, stop,
81+
// and query the service, so the GUI (running as a normal user) can manage
82+
// the service without needing elevation.
83+
//
84+
// Access mask:
85+
//
86+
// SERVICE_QUERY_STATUS = 0x0004
87+
// SERVICE_START = 0x0010
88+
// SERVICE_STOP = 0x0020
89+
// SERVICE_INTERROGATE = 0x0080
90+
// Total = 0x00B4
91+
//
92+
// SIDs: AU = Authenticated Users, BA = Built-in Administrators, SY = Local System.
93+
func setServiceDACL(serviceName string) error {
94+
sddl := "D:(A;;0x00B4;;;AU)(A;;0x01FF;;;BA)(A;;0x01FF;;;SY)"
95+
96+
sd, err := windows.SecurityDescriptorFromString(sddl)
97+
if err != nil {
98+
return fmt.Errorf("parse SDDL: %w", err)
99+
}
100+
defer windows.LocalFree(windows.Handle(unsafe.Pointer(sd)))
101+
102+
dacl, _, err := sd.DACL()
103+
if err != nil {
104+
return fmt.Errorf("get DACL: %w", err)
105+
}
106+
107+
return windows.SetNamedSecurityInfo(
108+
serviceName,
109+
windows.SE_SERVICE,
110+
windows.DACL_SECURITY_INFORMATION,
111+
nil, nil, dacl, nil,
112+
)
113+
}

0 commit comments

Comments
 (0)