@@ -5,7 +5,9 @@ package ipc
55import (
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
4961func 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