forked from winlabs/gowin32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjob.go
317 lines (294 loc) · 11.2 KB
/
job.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
/*
* Copyright (c) 2014-2017 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the license is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gowin32
import (
"github.com/winlabs/gowin32/wrappers"
"syscall"
"unsafe"
)
type JobLimitFlags uint32
const (
JobLimitWorkingSet JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_WORKINGSET
JobLimitProcessTime JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_PROCESS_TIME
JobLimitJobTime JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_JOB_TIME
JobLimitActiveProcess JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_ACTIVE_PROCESS
JobLimitAffinity JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_AFFINITY
JobLimitPriorityClass JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_PRIORITY_CLASS
JobLimitPreserveJobTime JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME
JobLimitSchedulingClass JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_SCHEDULING_CLASS
JobLimitProcessMemory JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_PROCESS_MEMORY
JobLimitDieOnUnhandledException JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
JobLimitBreakawayOK JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_BREAKAWAY_OK
JobLimitSilentBreakawayOK JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK
JobLimitKillOnJobClose JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
JobLimitSubsetAffinity JobLimitFlags = wrappers.JOB_OBJECT_LIMIT_SUBSET_AFFINITY
)
type JobBasicLimitInfo struct {
PerProcessUserTimeLimit int64
PerJobUserTimeLimit int64
LimitFlags JobLimitFlags
MinimumWorkingSetSize uintptr
MaximumWorkingSetSize uintptr
ActiveProcessLimit uint
Affinity uintptr
PriorityClass uint
SchedulingClass uint
}
type JobExtendedLimitInfo struct {
JobBasicLimitInfo
ProcessMemoryLimit uintptr
JobMemoryLimit uintptr
PeakProcessMemoryUsed uintptr
PeakJobMemoryUsed uintptr
}
type JobUILimitFlags uint32
const (
JobUILimitHandles JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_HANDLES
JobUILimitReadClipboard JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_READCLIPBOARD
JobUILimitWriteClipboard JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_WRITECLIPBOARD
JobUILimitSystemParameters JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS
JobUILimitDisplaySettings JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_DISPLAYSETTINGS
JobUILimitGlobalAtoms JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_GLOBALATOMS
JobUILimitDesktop JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_DESKTOP
JobUILimitExitWindows JobUILimitFlags = wrappers.JOB_OBJECT_UILIMIT_EXITWINDOWS
)
type Job struct {
handle syscall.Handle
}
func NewJob(name string) (*Job, error) {
var nameRaw *uint16
if name != "" {
nameRaw = syscall.StringToUTF16Ptr(name)
}
hJob, err := wrappers.CreateJobObject(nil, nameRaw)
if err != nil {
return nil, NewWindowsError("CreateJobObject", err)
}
return &Job{handle: hJob}, nil
}
func OpenJob(name string) (*Job, error) {
hJob, err := wrappers.OpenJobObject(
wrappers.JOB_OBJECT_ALL_ACCESS,
false,
syscall.StringToUTF16Ptr(name))
if err != nil {
return nil, NewWindowsError("OpenJobObject", err)
}
return &Job{handle: hJob}, nil
}
func (self *Job) Close() error {
if self.handle != 0 {
if err := wrappers.CloseHandle(self.handle); err != nil {
return NewWindowsError("CloseHandle", err)
}
self.handle = 0
}
return nil
}
func (self *Job) AssignProcess(pid uint) error {
hProcess, err := wrappers.OpenProcess(
wrappers.PROCESS_SET_QUOTA | wrappers.PROCESS_TERMINATE,
false,
uint32(pid))
if err != nil {
return NewWindowsError("OpenProcess", err)
}
defer wrappers.CloseHandle(hProcess)
if err := wrappers.AssignProcessToJobObject(self.handle, hProcess); err != nil {
return NewWindowsError("AssignProcessToJobObject", err)
}
return nil
}
func (self *Job) GetBasicLimitInfo() (*JobBasicLimitInfo, error) {
var info wrappers.JOBOBJECT_BASIC_LIMIT_INFORMATION
err := wrappers.QueryInformationJobObject(
self.handle,
wrappers.JobObjectBasicLimitInformation,
(*byte)(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)),
nil)
if err != nil {
return nil, NewWindowsError("QueryInformationJobObject", err)
}
return &JobBasicLimitInfo{
PerProcessUserTimeLimit: info.PerProcessUserTimeLimit,
PerJobUserTimeLimit: info.PerJobUserTimeLimit,
LimitFlags: JobLimitFlags(info.LimitFlags),
MinimumWorkingSetSize: info.MinimumWorkingSetSize,
MaximumWorkingSetSize: info.MaximumWorkingSetSize,
ActiveProcessLimit: uint(info.ActiveProcessLimit),
Affinity: info.Affinity,
PriorityClass: uint(info.PriorityClass),
SchedulingClass: uint(info.SchedulingClass),
}, nil
}
func (self *Job) GetExtendedLimitInfo() (*JobExtendedLimitInfo, error) {
var info wrappers.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
err := wrappers.QueryInformationJobObject(
self.handle,
wrappers.JobObjectExtendedLimitInformation,
(*byte)(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)),
nil)
if err != nil {
return nil, NewWindowsError("QueryInformationJobObject", err)
}
return &JobExtendedLimitInfo{
JobBasicLimitInfo: JobBasicLimitInfo{
PerProcessUserTimeLimit: info.BasicLimitInformation.PerProcessUserTimeLimit,
PerJobUserTimeLimit: info.BasicLimitInformation.PerJobUserTimeLimit,
LimitFlags: JobLimitFlags(info.BasicLimitInformation.LimitFlags),
MinimumWorkingSetSize: info.BasicLimitInformation.MinimumWorkingSetSize,
MaximumWorkingSetSize: info.BasicLimitInformation.MaximumWorkingSetSize,
ActiveProcessLimit: uint(info.BasicLimitInformation.ActiveProcessLimit),
Affinity: info.BasicLimitInformation.Affinity,
PriorityClass: uint(info.BasicLimitInformation.PriorityClass),
SchedulingClass: uint(info.BasicLimitInformation.SchedulingClass),
},
ProcessMemoryLimit: info.ProcessMemoryLimit,
JobMemoryLimit: info.JobMemoryLimit,
PeakProcessMemoryUsed: info.PeakProcessMemoryUsed,
PeakJobMemoryUsed: info.PeakJobMemoryUsed,
}, nil
}
func (self *Job) GetBasicUIRestrictions() (JobUILimitFlags, error) {
var info wrappers.JOBOBJECT_BASIC_UI_RESTRICTIONS
err := wrappers.QueryInformationJobObject(
self.handle,
wrappers.JobObjectBasicUIRestrictions,
(*byte)(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)),
nil)
if err != nil {
return 0, NewWindowsError("QueryInformationJobObject", err)
}
return JobUILimitFlags(info.UIRestrictionsClass), nil
}
func (self *Job) GetProcesses() ([]uint, error) {
var info wrappers.JOBOBJECT_BASIC_PROCESS_ID_LIST
err := wrappers.QueryInformationJobObject(
self.handle,
wrappers.JobObjectBasicProcessIdList,
(*byte)(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)),
nil)
if err != nil && err != wrappers.ERROR_MORE_DATA {
return nil, NewWindowsError("QueryInformationJobObject", err)
}
buf := make([]byte, unsafe.Sizeof(info) + unsafe.Sizeof(info.ProcessIdList[0])*uintptr(info.NumberOfAssignedProcesses - 1))
err = wrappers.QueryInformationJobObject(
self.handle,
wrappers.JobObjectBasicProcessIdList,
&buf[0],
uint32(len(buf)),
nil)
if err != nil {
return nil, NewWindowsError("QueryInformationJobObject", err)
}
bufInfo := (*wrappers.JOBOBJECT_BASIC_PROCESS_ID_LIST)(unsafe.Pointer(&buf[0]))
rawPids := make([]uintptr, bufInfo.NumberOfProcessIdsInList)
wrappers.RtlMoveMemory(
(*byte)(unsafe.Pointer(&rawPids[0])),
(*byte)(unsafe.Pointer(&bufInfo.ProcessIdList[0])),
uintptr(bufInfo.NumberOfProcessIdsInList)*unsafe.Sizeof(rawPids[0]))
pids := make([]uint, bufInfo.NumberOfProcessIdsInList)
for i, rawPid := range rawPids {
pids[i] = uint(rawPid)
}
return pids, nil
}
func (self *Job) SetBasicLimitInfo(info *JobBasicLimitInfo) error {
rawInfo := wrappers.JOBOBJECT_BASIC_LIMIT_INFORMATION{
PerProcessUserTimeLimit: info.PerProcessUserTimeLimit,
PerJobUserTimeLimit: info.PerJobUserTimeLimit,
LimitFlags: uint32(info.LimitFlags),
MinimumWorkingSetSize: info.MinimumWorkingSetSize,
MaximumWorkingSetSize: info.MaximumWorkingSetSize,
ActiveProcessLimit: uint32(info.ActiveProcessLimit),
Affinity: info.Affinity,
PriorityClass: uint32(info.PriorityClass),
SchedulingClass: uint32(info.SchedulingClass),
}
err := wrappers.SetInformationJobObject(
self.handle,
wrappers.JobObjectBasicLimitInformation,
(*byte)(unsafe.Pointer(&rawInfo)),
uint32(unsafe.Sizeof(rawInfo)))
if err != nil {
return NewWindowsError("SetInformationJobObject", err)
}
return nil
}
func (self *Job) SetExtendedLimitInfo(info *JobExtendedLimitInfo) error {
rawInfo := wrappers.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
BasicLimitInformation: wrappers.JOBOBJECT_BASIC_LIMIT_INFORMATION{
PerProcessUserTimeLimit: info.PerProcessUserTimeLimit,
PerJobUserTimeLimit: info.PerJobUserTimeLimit,
LimitFlags: uint32(info.LimitFlags),
MinimumWorkingSetSize: info.MinimumWorkingSetSize,
MaximumWorkingSetSize: info.MaximumWorkingSetSize,
ActiveProcessLimit: uint32(info.ActiveProcessLimit),
Affinity: info.Affinity,
PriorityClass: uint32(info.PriorityClass),
SchedulingClass: uint32(info.SchedulingClass),
},
ProcessMemoryLimit: info.ProcessMemoryLimit,
JobMemoryLimit: info.JobMemoryLimit,
PeakProcessMemoryUsed: info.PeakProcessMemoryUsed,
PeakJobMemoryUsed: info.PeakJobMemoryUsed,
}
err := wrappers.SetInformationJobObject(
self.handle,
wrappers.JobObjectExtendedLimitInformation,
(*byte)(unsafe.Pointer(&rawInfo)),
uint32(unsafe.Sizeof(rawInfo)))
if err != nil {
return NewWindowsError("SetInformationJobObject", err)
}
return nil
}
func (self *Job) SetBasicUIRestrictions(flags JobUILimitFlags) error {
info := wrappers.JOBOBJECT_BASIC_UI_RESTRICTIONS{
UIRestrictionsClass: uint32(flags),
}
err := wrappers.SetInformationJobObject(
self.handle,
wrappers.JobObjectBasicUIRestrictions,
(*byte)(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)))
if err != nil {
return NewWindowsError("SetInformationJobObject", err)
}
return nil
}
func (self *Job) ProcessInJob(pid uint) (bool, error) {
hProcess, err := wrappers.OpenProcess(wrappers.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
if err != nil {
return false, NewWindowsError("OpenProcess", err)
}
defer wrappers.CloseHandle(hProcess)
var result bool
if err := wrappers.IsProcessInJob(hProcess, self.handle, &result); err != nil {
return false, NewWindowsError("IsProcessInJob", err)
}
return result, nil
}
func (self *Job) Terminate(exitCode uint) error {
if err := wrappers.TerminateJobObject(self.handle, uint32(exitCode)); err != nil {
return NewWindowsError("TerminateJobObject", err)
}
return nil
}