@@ -26,8 +26,12 @@ import (
26
26
"github.com/cenkalti/backoff/v4"
27
27
"github.com/rabbitstack/fibratus/pkg/alertsender"
28
28
"github.com/rabbitstack/fibratus/pkg/kevent"
29
+ "github.com/rabbitstack/fibratus/pkg/sys"
29
30
log "github.com/sirupsen/logrus"
31
+ "golang.org/x/sys/windows"
32
+ "math"
30
33
"os"
34
+ "path/filepath"
31
35
"time"
32
36
)
33
37
@@ -45,6 +49,7 @@ const systrayPipe = `\\.\pipe\fibratus-systray`
45
49
// by the systray sender.
46
50
type systray struct {
47
51
config Config
52
+ proc windows.Handle // systray server process handle
48
53
}
49
54
50
55
// MsgType determines the type of the message sent
@@ -85,6 +90,78 @@ func makeSender(config alertsender.Config) (alertsender.Sender, error) {
85
90
return & systray {}, nil
86
91
}
87
92
93
+ // spin up systray server process
94
+ var si windows.StartupInfo
95
+ var pi windows.ProcessInformation
96
+
97
+ exe , err := os .Executable ()
98
+ if err != nil {
99
+ return nil , err
100
+ }
101
+ cmdline := filepath .Join (filepath .Dir (exe ), "fibratus-systray.exe" )
102
+ argv , err := windows .UTF16PtrFromString (cmdline )
103
+ if err != nil {
104
+ return nil , err
105
+ }
106
+
107
+ log .Infof ("starting systray server process %q" , cmdline )
108
+
109
+ if sys .IsWindowsService () {
110
+ // if we're running inside Windows Service, the systray server
111
+ // process must be created in the console session and with the
112
+ // currently logged-in user access token
113
+ var token windows.Token
114
+
115
+ // enable TCB privilege to obtain the console session user token
116
+ sys .SetTcbPrivilege ()
117
+
118
+ consoleSessionID := sys .WTSGetActiveConsoleSessionID ()
119
+ if consoleSessionID == math .MaxUint32 && sys .IsInSandbox () {
120
+ // if we failed to obtain the console session ID and
121
+ // are running inside Windows Sandbox, use session 1
122
+ consoleSessionID = 1
123
+ }
124
+
125
+ if sys .WTSQueryUserToken (consoleSessionID , & token ) {
126
+ log .Infof ("obtained user token for console session ID %d" , consoleSessionID )
127
+ err = windows .CreateProcessAsUser (
128
+ token ,
129
+ nil ,
130
+ argv ,
131
+ nil ,
132
+ nil ,
133
+ false ,
134
+ windows .CREATE_NO_WINDOW ,
135
+ nil ,
136
+ nil ,
137
+ & si ,
138
+ & pi ,
139
+ )
140
+ } else {
141
+ err = fmt .Errorf ("unable to obtain user token for console session ID %d" , consoleSessionID )
142
+ }
143
+
144
+ // drop TCB privilege and close the token handle
145
+ sys .RemoveTcbPrivilege ()
146
+ _ = windows .CloseHandle (windows .Handle (token ))
147
+ } else {
148
+ err = windows .CreateProcess (
149
+ nil ,
150
+ argv ,
151
+ nil ,
152
+ nil ,
153
+ false ,
154
+ windows .CREATE_NO_WINDOW ,
155
+ nil ,
156
+ nil ,
157
+ & si ,
158
+ & pi )
159
+ }
160
+
161
+ if err != nil {
162
+ log .Warnf ("unable to start systray server process: %v" , err )
163
+ }
164
+
88
165
s := & systray {config : c }
89
166
b := & backoff.ExponentialBackOff {
90
167
// first backoff timeout will be somewhere in the 100 - 300 ms range given the default multiplier
@@ -112,6 +189,10 @@ func makeSender(config alertsender.Config) (alertsender.Sender, error) {
112
189
break
113
190
}
114
191
192
+ log .Info ("established connection to systray server" )
193
+
194
+ s .proc = pi .Process
195
+
115
196
return s , s .send (& Msg {Type : Conf , Data : c })
116
197
}
117
198
@@ -124,11 +205,17 @@ func (s *systray) Send(alert alertsender.Alert) error {
124
205
func (* systray ) Type () alertsender.Type { return alertsender .Systray }
125
206
func (* systray ) SupportsMarkdown () bool { return false }
126
207
127
- func (s * systray ) Shutdown () error { return nil }
208
+ func (s * systray ) Shutdown () error {
209
+ if s .proc != 0 && sys .IsProcessRunning (s .proc ) {
210
+ return windows .TerminateProcess (s .proc , 1 )
211
+ }
212
+ return nil
213
+ }
128
214
129
215
func (s * systray ) send (m * Msg ) error {
130
216
ctx , cancel := context .WithTimeout (context .Background (), time .Second * 5 )
131
217
defer cancel ()
218
+
132
219
conn , err := winio .DialPipeContext (ctx , systrayPipe )
133
220
if err != nil {
134
221
return fmt .Errorf ("unable to dial %s pipe: %v" , systrayPipe , err )
@@ -151,6 +238,5 @@ func (s *systray) send(m *Msg) error {
151
238
152
239
func pipeExists () bool {
153
240
_ , err := os .Stat (systrayPipe )
154
- log .Warnf ("pipe not found: %v" , err )
155
241
return err == nil
156
242
}
0 commit comments