win32-taskscheduler 1.0.12 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -8
- data/VERSION +2 -1
- data/lib/win32/taskscheduler.rb +8 -6
- data/lib/win32/taskscheduler/constants.rb +266 -0
- data/lib/win32/taskscheduler/helper.rb +51 -0
- data/lib/win32/taskscheduler/sid.rb +132 -0
- data/lib/win32/taskscheduler/time_calc_helper.rb +132 -0
- data/lib/win32/taskscheduler/version.rb +1 -1
- data/spec/functional/win32/{windows → taskscheduler}/time_calc_helper_spec.rb +3 -3
- data/spec/functional/win32/{taskschedular_spec.rb → taskscheduler_spec.rb} +0 -0
- data/spec/unit/win32/{windows → taskscheduler}/constants_spec.rb +1 -1
- data/spec/unit/win32/{windows → taskscheduler}/time_calc_helper_spec.rb +3 -3
- data/spec/unit/win32/{taskschedular_spec.rb → taskscheduler_spec.rb} +0 -0
- metadata +10 -10
- data/lib/win32/windows/constants.rb +0 -263
- data/lib/win32/windows/helper.rb +0 -49
- data/lib/win32/windows/sid.rb +0 -128
- data/lib/win32/windows/time_calc_helper.rb +0 -130
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90e586400ec6f09917fb684fb99037a5bd2c332995ecb72c43b37222ce9e2a61
|
4
|
+
data.tar.gz: 7a081a2f498d932400424d87885a89d2f58126366f6d51d6a361d97a215c18c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fffce988d9274141c58c5cfce948ac86ff9b03396313f2721ce7bea08c6daf3a118be23418a2b56c7cfa58628db4e7e3943c0fe011b2b7da343007f80c9a7d0
|
7
|
+
data.tar.gz: 7a282afc6a6be4a3cefd6258ee33257221e2d397c327f07329b1972178165c64df0ef89b3c408c951db7375a85a5afea4a0ad63895f3a449036f18b94db13ae4
|
data/CHANGELOG.md
CHANGED
@@ -4,27 +4,33 @@ Note: this log contains only changes from win32-taskscheduler release 0.4.0 and
|
|
4
4
|
-- it does not contain the changes from prior releases. To view change history
|
5
5
|
prior to release 0.4.0, please visit the [source repository](https://github.com/chef/win32-taskscheduler/commits).
|
6
6
|
|
7
|
-
<!-- latest_release
|
8
|
-
##
|
7
|
+
<!-- latest_release unreleased -->
|
8
|
+
## Unreleased
|
9
9
|
|
10
10
|
#### Merged Pull Requests
|
11
|
-
-
|
11
|
+
- Bump version to 2.0 [#71](https://github.com/chef/win32-taskscheduler/pull/71) ([btm](https://github.com/btm))
|
12
12
|
<!-- latest_release -->
|
13
13
|
|
14
|
-
<!-- release_rollup since=1.0.
|
15
|
-
### Changes since 1.0.
|
14
|
+
<!-- release_rollup since=1.0.12 -->
|
15
|
+
### Changes since 1.0.12 release
|
16
16
|
|
17
17
|
#### Merged Pull Requests
|
18
|
-
-
|
19
|
-
-
|
18
|
+
- Bump version to 2.0 [#71](https://github.com/chef/win32-taskscheduler/pull/71) ([btm](https://github.com/btm)) <!-- 2.0.0 -->
|
19
|
+
- Move helpers under the Win32::TaskScheduler namespace [#70](https://github.com/chef/win32-taskscheduler/pull/70) ([btm](https://github.com/btm)) <!-- 1.0.13 -->
|
20
20
|
<!-- release_rollup -->
|
21
21
|
|
22
22
|
<!-- latest_stable_release -->
|
23
|
+
## [win32-taskscheduler-1.0.12](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.12) (2018-10-11)
|
24
|
+
|
25
|
+
#### Merged Pull Requests
|
26
|
+
- Refactored configure_settings [#67](https://github.com/chef/win32-taskscheduler/pull/67) ([btm](https://github.com/btm))
|
27
|
+
- Fixing user registration at Non English version of windows [#69](https://github.com/chef/win32-taskscheduler/pull/69) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
|
28
|
+
<!-- latest_stable_release -->
|
29
|
+
|
23
30
|
## [win32-taskscheduler-1.0.10](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.10) (2018-07-24)
|
24
31
|
|
25
32
|
#### Merged Pull Requests
|
26
33
|
- Fix exists? method breaking task full path search [#62](https://github.com/chef/win32-taskscheduler/pull/62) ([Vasu1105](https://github.com/Vasu1105))
|
27
|
-
<!-- latest_stable_release -->
|
28
34
|
|
29
35
|
## [win32-taskscheduler-1.0.9](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.9) (2018-07-23)
|
30
36
|
|
data/VERSION
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
2
|
+
|
data/lib/win32/taskscheduler.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative '
|
3
|
-
require_relative '
|
1
|
+
require_relative 'taskscheduler/sid'
|
2
|
+
require_relative 'taskscheduler/helper'
|
3
|
+
require_relative 'taskscheduler/time_calc_helper'
|
4
|
+
require_relative 'taskscheduler/constants'
|
4
5
|
require_relative 'taskscheduler/version'
|
5
6
|
require 'win32ole'
|
6
7
|
require 'socket'
|
@@ -12,9 +13,10 @@ module Win32
|
|
12
13
|
|
13
14
|
# The TaskScheduler class encapsulates a Windows scheduled task
|
14
15
|
class TaskScheduler
|
15
|
-
include
|
16
|
-
include
|
17
|
-
include
|
16
|
+
include Win32::TaskScheduler::Helper
|
17
|
+
include Win32::TaskScheduler::TaskSchedulerConstants
|
18
|
+
include Win32::TaskScheduler::TimeCalcHelper
|
19
|
+
include Win32::TaskScheduler::SID
|
18
20
|
|
19
21
|
# The Error class is typically raised if any TaskScheduler methods fail.
|
20
22
|
class Error < StandardError; end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require_relative 'sid'
|
2
|
+
|
3
|
+
module Win32
|
4
|
+
class TaskScheduler
|
5
|
+
module TaskSchedulerConstants
|
6
|
+
SERVICE_ACCOUNT_USERS = SID::SERVICE_ACCOUNT_USERS
|
7
|
+
|
8
|
+
BUILT_IN_GROUPS = SID::BUILT_IN_GROUPS
|
9
|
+
|
10
|
+
SYSTEM_USERS = SERVICE_ACCOUNT_USERS + BUILT_IN_GROUPS
|
11
|
+
|
12
|
+
# Triggers
|
13
|
+
|
14
|
+
# Trigger is set to run the task a single time
|
15
|
+
TASK_TIME_TRIGGER_ONCE = 1
|
16
|
+
|
17
|
+
# Trigger is set to run the task on a daily interval
|
18
|
+
TASK_TIME_TRIGGER_DAILY = 2
|
19
|
+
|
20
|
+
# Trigger is set to run the task on specific days of a specific week & month
|
21
|
+
TASK_TIME_TRIGGER_WEEKLY = 3
|
22
|
+
|
23
|
+
# Trigger is set to run the task on specific day(s) of the month
|
24
|
+
TASK_TIME_TRIGGER_MONTHLYDATE = 4
|
25
|
+
|
26
|
+
# Trigger is set to run the task on specific day(s) of the month
|
27
|
+
TASK_TIME_TRIGGER_MONTHLYDOW = 5
|
28
|
+
|
29
|
+
# Trigger is set to run the task if the system remains idle for the amount
|
30
|
+
# of time specified by the idle wait time of the task
|
31
|
+
TASK_EVENT_TRIGGER_ON_IDLE = 6
|
32
|
+
|
33
|
+
TASK_TRIGGER_REGISTRATION = 7
|
34
|
+
|
35
|
+
# Trigger is set to run the task at system startup
|
36
|
+
TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 8
|
37
|
+
|
38
|
+
# Trigger is set to run the task when a user logs on
|
39
|
+
TASK_EVENT_TRIGGER_AT_LOGON = 9
|
40
|
+
|
41
|
+
TASK_TRIGGER_SESSION_STATE_CHANGE = 11
|
42
|
+
|
43
|
+
# Daily Tasks
|
44
|
+
|
45
|
+
# The task will run on Sunday
|
46
|
+
TASK_SUNDAY = 0x1
|
47
|
+
|
48
|
+
# The task will run on Monday
|
49
|
+
TASK_MONDAY = 0x2
|
50
|
+
|
51
|
+
# The task will run on Tuesday
|
52
|
+
TASK_TUESDAY = 0x4
|
53
|
+
|
54
|
+
# The task will run on Wednesday
|
55
|
+
TASK_WEDNESDAY = 0x8
|
56
|
+
|
57
|
+
# The task will run on Thursday
|
58
|
+
TASK_THURSDAY = 0x10
|
59
|
+
|
60
|
+
# The task will run on Friday
|
61
|
+
TASK_FRIDAY = 0x20
|
62
|
+
|
63
|
+
# The task will run on Saturday
|
64
|
+
TASK_SATURDAY = 0x40
|
65
|
+
|
66
|
+
# Weekly tasks
|
67
|
+
|
68
|
+
# The task will run between the 1st and 7th day of the month
|
69
|
+
TASK_FIRST_WEEK = 0x01
|
70
|
+
|
71
|
+
# The task will run between the 8th and 14th day of the month
|
72
|
+
TASK_SECOND_WEEK = 0x02
|
73
|
+
|
74
|
+
# The task will run between the 15th and 21st day of the month
|
75
|
+
TASK_THIRD_WEEK = 0x04
|
76
|
+
|
77
|
+
# The task will run between the 22nd and 28th day of the month
|
78
|
+
TASK_FOURTH_WEEK = 0x08
|
79
|
+
|
80
|
+
# The task will run the last seven days of the month
|
81
|
+
TASK_LAST_WEEK = 0x10
|
82
|
+
|
83
|
+
# Monthly tasks
|
84
|
+
|
85
|
+
# The task will run in January
|
86
|
+
TASK_JANUARY = 0x1
|
87
|
+
|
88
|
+
# The task will run in February
|
89
|
+
TASK_FEBRUARY = 0x2
|
90
|
+
|
91
|
+
# The task will run in March
|
92
|
+
TASK_MARCH = 0x4
|
93
|
+
|
94
|
+
# The task will run in April
|
95
|
+
TASK_APRIL = 0x8
|
96
|
+
|
97
|
+
# The task will run in May
|
98
|
+
TASK_MAY = 0x10
|
99
|
+
|
100
|
+
# The task will run in June
|
101
|
+
TASK_JUNE = 0x20
|
102
|
+
|
103
|
+
# The task will run in July
|
104
|
+
TASK_JULY = 0x40
|
105
|
+
|
106
|
+
# The task will run in August
|
107
|
+
TASK_AUGUST = 0x80
|
108
|
+
|
109
|
+
# The task will run in September
|
110
|
+
TASK_SEPTEMBER = 0x100
|
111
|
+
|
112
|
+
# The task will run in October
|
113
|
+
TASK_OCTOBER = 0x200
|
114
|
+
|
115
|
+
# The task will run in November
|
116
|
+
TASK_NOVEMBER = 0x400
|
117
|
+
|
118
|
+
# The task will run in December
|
119
|
+
TASK_DECEMBER = 0x800
|
120
|
+
|
121
|
+
# Flags
|
122
|
+
|
123
|
+
# Used when converting AT service jobs into work items
|
124
|
+
TASK_FLAG_INTERACTIVE = 0x1
|
125
|
+
|
126
|
+
# The work item will be deleted when there are no more scheduled run times
|
127
|
+
TASK_FLAG_DELETE_WHEN_DONE = 0x2
|
128
|
+
|
129
|
+
# The work item is disabled. Useful for temporarily disabling a task
|
130
|
+
TASK_FLAG_DISABLED = 0x4
|
131
|
+
|
132
|
+
# The work item begins only if the computer is not in use at the scheduled
|
133
|
+
# start time
|
134
|
+
TASK_FLAG_START_ONLY_IF_IDLE = 0x10
|
135
|
+
|
136
|
+
# The work item terminates if the computer makes an idle to non-idle
|
137
|
+
# transition while the work item is running
|
138
|
+
TASK_FLAG_KILL_ON_IDLE_END = 0x20
|
139
|
+
|
140
|
+
# The work item does not start if the computer is running on battery power
|
141
|
+
TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
|
142
|
+
|
143
|
+
# The work item ends, and the associated application quits, if the computer
|
144
|
+
# switches to battery power
|
145
|
+
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
|
146
|
+
|
147
|
+
# The work item starts only if the computer is in a docking station
|
148
|
+
TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
|
149
|
+
|
150
|
+
# The work item created will be hidden
|
151
|
+
TASK_FLAG_HIDDEN = 0x200
|
152
|
+
|
153
|
+
# The work item runs only if there is a valid internet connection
|
154
|
+
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
|
155
|
+
|
156
|
+
# The work item starts again if the computer makes a non-idle to idle
|
157
|
+
# transition
|
158
|
+
TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
|
159
|
+
|
160
|
+
# The work item causes the system to be resumed, or awakened, if the
|
161
|
+
# system is running on batter power
|
162
|
+
TASK_FLAG_SYSTEM_REQUIRED = 0x1000
|
163
|
+
|
164
|
+
# The work item runs only if a specified account is logged on interactively
|
165
|
+
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
|
166
|
+
|
167
|
+
# Triggers
|
168
|
+
|
169
|
+
# The task will stop at some point in time
|
170
|
+
TASK_TRIGGER_FLAG_HAS_END_DATE = 0x1
|
171
|
+
|
172
|
+
# The task can be stopped at the end of the repetition period
|
173
|
+
TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0x2
|
174
|
+
|
175
|
+
# The task trigger is disabled
|
176
|
+
TASK_TRIGGER_FLAG_DISABLED = 0x4
|
177
|
+
|
178
|
+
# Run Level Types
|
179
|
+
# Tasks will be run with the least privileges
|
180
|
+
TASK_RUNLEVEL_LUA = 0
|
181
|
+
# Tasks will be run with the highest privileges
|
182
|
+
TASK_RUNLEVEL_HIGHEST = 1
|
183
|
+
|
184
|
+
# Logon Types
|
185
|
+
# Used for non-NT credentials
|
186
|
+
TASK_LOGON_NONE = 0
|
187
|
+
# Use a password for logging on the user
|
188
|
+
TASK_LOGON_PASSWORD = 1
|
189
|
+
# The service will log the user on using Service For User
|
190
|
+
TASK_LOGON_S4U = 2
|
191
|
+
# Task will be run only in an existing interactive session
|
192
|
+
TASK_LOGON_INTERACTIVE_TOKEN = 3
|
193
|
+
# Group activation. The groupId field specifies the group
|
194
|
+
TASK_LOGON_GROUP = 4
|
195
|
+
# When Local System, Local Service, or Network Service account is
|
196
|
+
# being used as a security context to run the task
|
197
|
+
TASK_LOGON_SERVICE_ACCOUNT = 5
|
198
|
+
# Not in use; currently identical to TASK_LOGON_PASSWORD
|
199
|
+
TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6
|
200
|
+
|
201
|
+
TASK_MAX_RUN_TIMES = 1440
|
202
|
+
TASKS_TO_RETRIEVE = 5
|
203
|
+
|
204
|
+
# Task creation
|
205
|
+
|
206
|
+
TASK_VALIDATE_ONLY = 0x1
|
207
|
+
TASK_CREATE = 0x2
|
208
|
+
TASK_UPDATE = 0x4
|
209
|
+
TASK_CREATE_OR_UPDATE = 0x6
|
210
|
+
TASK_DISABLE = 0x8
|
211
|
+
TASK_DONT_ADD_PRINCIPAL_ACE = 0x10
|
212
|
+
TASK_IGNORE_REGISTRATION_TRIGGERS = 0x20
|
213
|
+
|
214
|
+
# Priority classes
|
215
|
+
|
216
|
+
REALTIME_PRIORITY_CLASS = 0
|
217
|
+
HIGH_PRIORITY_CLASS = 1
|
218
|
+
ABOVE_NORMAL_PRIORITY_CLASS = 2 # Or 3
|
219
|
+
NORMAL_PRIORITY_CLASS = 4 # Or 5, 6
|
220
|
+
BELOW_NORMAL_PRIORITY_CLASS = 7 # Or 8
|
221
|
+
IDLE_PRIORITY_CLASS = 9 # Or 10
|
222
|
+
|
223
|
+
CLSCTX_INPROC_SERVER = 0x1
|
224
|
+
CLSID_CTask = [0x148BD520, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
225
|
+
CLSID_CTaskScheduler = [0x148BD52A, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
226
|
+
IID_ITaskScheduler = [0x148BD527, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
227
|
+
IID_ITask = [0x148BD524, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
228
|
+
IID_IPersistFile = [0x0000010b, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSC8')
|
229
|
+
|
230
|
+
# Days of month
|
231
|
+
|
232
|
+
TASK_FIRST = 0x01
|
233
|
+
TASK_SECOND = 0x02
|
234
|
+
TASK_THIRD = 0x04
|
235
|
+
TASK_FOURTH = 0x08
|
236
|
+
TASK_FIFTH = 0x10
|
237
|
+
TASK_SIXTH = 0x20
|
238
|
+
TASK_SEVENTH = 0x40
|
239
|
+
TASK_EIGHTH = 0x80
|
240
|
+
TASK_NINETH = 0x100
|
241
|
+
TASK_TENTH = 0x200
|
242
|
+
TASK_ELEVENTH = 0x400
|
243
|
+
TASK_TWELFTH = 0x800
|
244
|
+
TASK_THIRTEENTH = 0x1000
|
245
|
+
TASK_FOURTEENTH = 0x2000
|
246
|
+
TASK_FIFTEENTH = 0x4000
|
247
|
+
TASK_SIXTEENTH = 0x8000
|
248
|
+
TASK_SEVENTEENTH = 0x10000
|
249
|
+
TASK_EIGHTEENTH = 0x20000
|
250
|
+
TASK_NINETEENTH = 0x40000
|
251
|
+
TASK_TWENTIETH = 0x80000
|
252
|
+
TASK_TWENTY_FIRST = 0x100000
|
253
|
+
TASK_TWENTY_SECOND = 0x200000
|
254
|
+
TASK_TWENTY_THIRD = 0x400000
|
255
|
+
TASK_TWENTY_FOURTH = 0x800000
|
256
|
+
TASK_TWENTY_FIFTH = 0x1000000
|
257
|
+
TASK_TWENTY_SIXTH = 0x2000000
|
258
|
+
TASK_TWENTY_SEVENTH = 0x4000000
|
259
|
+
TASK_TWENTY_EIGHTH = 0x8000000
|
260
|
+
TASK_TWENTY_NINTH = 0x10000000
|
261
|
+
TASK_THIRTYETH = 0x20000000
|
262
|
+
TASK_THIRTY_FIRST = 0x40000000
|
263
|
+
TASK_LAST = 0x80000000
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module Win32
|
4
|
+
class TaskScheduler
|
5
|
+
module Helper
|
6
|
+
extend FFI::Library
|
7
|
+
|
8
|
+
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
|
9
|
+
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
|
10
|
+
FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF
|
11
|
+
|
12
|
+
ffi_lib :kernel32, :advapi32
|
13
|
+
|
14
|
+
attach_function :FormatMessage, :FormatMessageA,
|
15
|
+
[:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
|
16
|
+
|
17
|
+
attach_function :ConvertStringSidToSidW, [ :pointer, :pointer ], :bool
|
18
|
+
attach_function :LookupAccountSidW, [ :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer ], :bool
|
19
|
+
attach_function :LocalFree, [ :pointer ], :pointer
|
20
|
+
|
21
|
+
def win_error(function, err=FFI.errno)
|
22
|
+
err_msg = ''
|
23
|
+
flags = FORMAT_MESSAGE_IGNORE_INSERTS |
|
24
|
+
FORMAT_MESSAGE_FROM_SYSTEM |
|
25
|
+
FORMAT_MESSAGE_MAX_WIDTH_MASK
|
26
|
+
|
27
|
+
# 0x0409 == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
|
28
|
+
# We use English for errors because Ruby uses English for errors.
|
29
|
+
|
30
|
+
FFI::MemoryPointer.new(:char, 1024) do |buf|
|
31
|
+
len = FormatMessage(flags, nil, err , 0x0409, buf, buf.size, nil)
|
32
|
+
err_msg = function + ': ' + buf.read_string(len).strip
|
33
|
+
end
|
34
|
+
|
35
|
+
err_msg
|
36
|
+
end
|
37
|
+
|
38
|
+
def ole_error(function, err)
|
39
|
+
regex = /OLE error code:(.*?)\sin/
|
40
|
+
match = regex.match(err.to_s)
|
41
|
+
|
42
|
+
if match
|
43
|
+
error = match.captures.first.hex
|
44
|
+
win_error(function, error)
|
45
|
+
else
|
46
|
+
"#{function}: #{err.to_s}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
class Pointer
|
5
|
+
def read_wstring(num_wchars = nil)
|
6
|
+
if num_wchars.nil?
|
7
|
+
# Find the length of the string
|
8
|
+
length = 0
|
9
|
+
last_char = nil
|
10
|
+
while last_char != "\000\000"
|
11
|
+
length += 1
|
12
|
+
last_char = get_bytes(0, length * 2)[-2..-1]
|
13
|
+
end
|
14
|
+
|
15
|
+
num_wchars = length
|
16
|
+
end
|
17
|
+
|
18
|
+
wide_to_utf8(get_bytes(0, num_wchars * 2))
|
19
|
+
end
|
20
|
+
|
21
|
+
def wide_to_utf8(wstring)
|
22
|
+
# ensure it is actually UTF-16LE
|
23
|
+
# Ruby likes to mark binary data as ASCII-8BIT
|
24
|
+
wstring = wstring.force_encoding('UTF-16LE')
|
25
|
+
|
26
|
+
# encode it all as UTF-8 and remove trailing CRLF and NULL characters
|
27
|
+
wstring.encode('UTF-8').strip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module Win32
|
33
|
+
class TaskScheduler
|
34
|
+
module SID
|
35
|
+
extend Win32::TaskScheduler::Helper
|
36
|
+
ERROR_INSUFFICIENT_BUFFER = 122
|
37
|
+
|
38
|
+
def self.LocalSystem
|
39
|
+
from_string_sid('S-1-5-18')
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.NtLocal
|
43
|
+
from_string_sid('S-1-5-19')
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.NtNetwork
|
47
|
+
from_string_sid('S-1-5-20')
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.BuiltinAdministrators
|
51
|
+
from_string_sid('S-1-5-32-544')
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.BuiltinUsers
|
55
|
+
from_string_sid('S-1-5-32-545')
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.Guests
|
59
|
+
from_string_sid('S-1-5-32-546')
|
60
|
+
end
|
61
|
+
|
62
|
+
# Converts a string-format security identifier (SID) into a valid, functional SID
|
63
|
+
# and returns a hash with account_name and account_simple_name
|
64
|
+
# @see https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsidtosidw
|
65
|
+
#
|
66
|
+
def self.from_string_sid(string_sid)
|
67
|
+
result = FFI::MemoryPointer.new :pointer
|
68
|
+
unless ConvertStringSidToSidW(utf8_to_wide(string_sid), result)
|
69
|
+
raise FFI::LastError.error
|
70
|
+
end
|
71
|
+
result_pointer = result.read_pointer
|
72
|
+
domain, name, use = account(result_pointer)
|
73
|
+
LocalFree(result_pointer)
|
74
|
+
account_names(domain, name, use)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.utf8_to_wide(ustring)
|
78
|
+
# ensure it is actually UTF-8
|
79
|
+
# Ruby likes to mark binary data as ASCII-8BIT
|
80
|
+
ustring = (ustring + '').force_encoding('UTF-8')
|
81
|
+
|
82
|
+
# ensure we have the double-null termination Windows Wide likes
|
83
|
+
ustring += "\000\000" if ustring.empty? || ustring[-1].chr != "\000"
|
84
|
+
|
85
|
+
# encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
|
86
|
+
ustring.encode('UTF-16LE')
|
87
|
+
end
|
88
|
+
|
89
|
+
# Accepts a security identifier (SID) as input.
|
90
|
+
# It retrieves the name of the account for this SID and the name of the
|
91
|
+
# first domain on which this SID is found
|
92
|
+
# @see https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-lookupaccountsidw
|
93
|
+
#
|
94
|
+
def self.account(sid)
|
95
|
+
sid = sid.pointer if sid.respond_to?(:pointer)
|
96
|
+
name_size = FFI::Buffer.new(:long).write_long(0)
|
97
|
+
referenced_domain_name_size = FFI::Buffer.new(:long).write_long(0)
|
98
|
+
|
99
|
+
if LookupAccountSidW(nil, sid, nil, name_size, nil, referenced_domain_name_size, nil)
|
100
|
+
raise 'Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountSid, and got no error!'
|
101
|
+
elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
|
102
|
+
raise FFI::LastError.error
|
103
|
+
end
|
104
|
+
|
105
|
+
name = FFI::MemoryPointer.new :char, (name_size.read_long * 2)
|
106
|
+
referenced_domain_name = FFI::MemoryPointer.new :char, (referenced_domain_name_size.read_long * 2)
|
107
|
+
use = FFI::Buffer.new(:long).write_long(0)
|
108
|
+
unless LookupAccountSidW(nil, sid, name, name_size, referenced_domain_name, referenced_domain_name_size, use)
|
109
|
+
raise FFI::LastError.error
|
110
|
+
end
|
111
|
+
[referenced_domain_name.read_wstring(referenced_domain_name_size.read_long), name.read_wstring(name_size.read_long), use.read_long]
|
112
|
+
end
|
113
|
+
|
114
|
+
# Formats domain, name and returns a hash with
|
115
|
+
# account_name and account_simple_name
|
116
|
+
#
|
117
|
+
def self.account_names(domain, name, _use)
|
118
|
+
account_name = !domain.to_s.empty? ? "#{domain}\\#{name}" : name
|
119
|
+
account_simple_name = name
|
120
|
+
{ account_name: account_name, account_simple_name: account_simple_name }
|
121
|
+
end
|
122
|
+
|
123
|
+
SERVICE_ACCOUNT_USERS = [self.LocalSystem, self.NtLocal, self.NtNetwork].map do |user|
|
124
|
+
[user[:account_simple_name].upcase, user[:account_name].upcase]
|
125
|
+
end.flatten.freeze
|
126
|
+
|
127
|
+
BUILT_IN_GROUPS = [self.BuiltinAdministrators, self.BuiltinUsers, self.Guests].map do |user|
|
128
|
+
[user[:account_simple_name].upcase, user[:account_name].upcase]
|
129
|
+
end.flatten.freeze
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Win32
|
2
|
+
class TaskScheduler
|
3
|
+
module TimeCalcHelper
|
4
|
+
|
5
|
+
# Returns actual no of days for given month;
|
6
|
+
# Array with a 0 is defined to give actual result without
|
7
|
+
# any manipulation. eg, DAYS_IN_A_MONTH[1] = 31
|
8
|
+
# 0(NUMBER) is kept to avoid exceptions during calculations
|
9
|
+
DAYS_IN_A_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
10
|
+
|
11
|
+
# Returns no of days in a given month of a year
|
12
|
+
def days_in_month(month, year)
|
13
|
+
(month == 2 && is_leap_year?(year)) ? 29 : DAYS_IN_A_MONTH[month]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Year is leap when it is a multiple of 4 and not a multiple of 100.
|
17
|
+
# But it can be a multiple of 400
|
18
|
+
def is_leap_year?(year)
|
19
|
+
(((year % 4).zero? && !(year % 100).zero?) || (year % 400).zero?)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns total time duration in minutes
|
23
|
+
def time_in_minutes(time_str)
|
24
|
+
time_in_seconds(time_str) / 60
|
25
|
+
end
|
26
|
+
|
27
|
+
# Calculates total time duration in seconds
|
28
|
+
def time_in_seconds(time_str)
|
29
|
+
dt_tm_hash = time_details(time_str)
|
30
|
+
curr_time = Time.now
|
31
|
+
|
32
|
+
# Basic time variables
|
33
|
+
future_year = curr_time.year + dt_tm_hash[:year].to_i
|
34
|
+
future_month = curr_time.month + dt_tm_hash[:month].to_i
|
35
|
+
future_day = curr_time.day + dt_tm_hash[:day].to_i
|
36
|
+
future_hr = curr_time.hour + dt_tm_hash[:hour].to_i
|
37
|
+
future_min = curr_time.min + dt_tm_hash[:min].to_i
|
38
|
+
future_sec = curr_time.sec + dt_tm_hash[:sec].to_i
|
39
|
+
|
40
|
+
# 'extra value' calculations for these time variables
|
41
|
+
future_sec, future_min = extra_time(future_sec, future_min, 60)
|
42
|
+
future_min, future_hr = extra_time(future_min, future_hr, 60)
|
43
|
+
future_hr, future_day = extra_time(future_hr, future_day, 24)
|
44
|
+
|
45
|
+
# explicit method to calculate overloaded days;
|
46
|
+
# They may stretch upto years; heance leap year & months are into consideration
|
47
|
+
future_day, future_month, future_year = extra_days(future_day, future_month, future_year, curr_time.month, curr_time.year)
|
48
|
+
|
49
|
+
future_month, future_year = extra_months(future_month, future_year, curr_time.month, curr_time.year)
|
50
|
+
|
51
|
+
future_time = Time.new(future_year, future_month, future_day, future_hr, future_min, future_sec)
|
52
|
+
|
53
|
+
# Difference in time will return seconds
|
54
|
+
future_time.to_i - curr_time.to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
# a will contain extra value of low_rank (in high_rank(eg min));
|
58
|
+
# b will hold actual low_rank value(ie sec) Example:
|
59
|
+
# low_rank = 65, high_rank = 2, div_val = 60
|
60
|
+
# Hence a = 1; b = 5
|
61
|
+
def extra_time(low_rank, high_rank, div_val)
|
62
|
+
a, b = low_rank.divmod(div_val)
|
63
|
+
high_rank += a; low_rank = b
|
64
|
+
[low_rank, high_rank]
|
65
|
+
end
|
66
|
+
|
67
|
+
def extra_months(month_count, year_count, init_month, init_year)
|
68
|
+
year, month_count = month_count.divmod(12)
|
69
|
+
if year.positive? && month_count.zero?
|
70
|
+
month_count = 12
|
71
|
+
year -= 1
|
72
|
+
end
|
73
|
+
year_count += year
|
74
|
+
[month_count, year_count]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns no of actual days with all overloaded months & Years
|
78
|
+
def extra_days(days_count, month_count, year_count, init_month, init_year)
|
79
|
+
# Will keep increamenting them with surplus days
|
80
|
+
days = days_count
|
81
|
+
mth = init_month
|
82
|
+
yr = init_year
|
83
|
+
|
84
|
+
loop do
|
85
|
+
days -= days_in_month(mth, yr)
|
86
|
+
break if days <= 0
|
87
|
+
mth += 1
|
88
|
+
if mth > 12
|
89
|
+
mth = 1; yr += 1
|
90
|
+
end
|
91
|
+
days_count = days
|
92
|
+
end
|
93
|
+
|
94
|
+
# Setting actual incremented values
|
95
|
+
month_count += (mth - init_month)
|
96
|
+
year_count += (yr - init_year)
|
97
|
+
|
98
|
+
[days_count, month_count, year_count]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Extracts "P_Y_M_DT_H_M_S" format and
|
102
|
+
# Returns a hash with applicable values of
|
103
|
+
# (keys =>) [:year, :month, :day, :hour, :min, :sec]
|
104
|
+
# Example: "PT3S" => {sec: 3}
|
105
|
+
def time_details(time_str)
|
106
|
+
tm_detail = {}
|
107
|
+
if time_str.to_s != ''
|
108
|
+
# time_str will be like "PxxYxxMxxDTxxHxxMxxS"
|
109
|
+
# Ignoring 'P' and extracting date and time
|
110
|
+
dt, tm = time_str[1..-1].split('T')
|
111
|
+
|
112
|
+
# Replacing strings
|
113
|
+
if dt.to_s != ''
|
114
|
+
dt['Y'] = 'year' if dt['Y']; dt['M'] = 'month' if dt['M']; dt['D'] = 'day' if dt['D']
|
115
|
+
dt_tm_array_to_hash(dt, tm_detail)
|
116
|
+
end
|
117
|
+
|
118
|
+
if tm.to_s != ''
|
119
|
+
tm['H'] = 'hour' if tm['H']; tm['M'] = 'min' if tm['M']; tm['S'] = 'sec' if tm['S']
|
120
|
+
dt_tm_array_to_hash(tm, tm_detail)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
tm_detail
|
124
|
+
end
|
125
|
+
|
126
|
+
# Method to convert date/time array to hash
|
127
|
+
def dt_tm_array_to_hash(arr, tm_detail)
|
128
|
+
arr.split(/(\d+)/)[1..-1].each_slice(2).inject(tm_detail) { |h, i| h[i.last.to_sym] = i.first; h }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require 'win32/
|
1
|
+
require 'win32/taskscheduler/time_calc_helper'
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
RSpec.describe
|
4
|
+
RSpec.describe Win32::TaskScheduler::TimeCalcHelper do
|
5
5
|
let(:object) { klass.new }
|
6
6
|
let(:klass) do
|
7
7
|
Class.new do
|
8
|
-
include
|
8
|
+
include Win32::TaskScheduler::TimeCalcHelper
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
File without changes
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'win32/
|
2
|
+
require 'win32/taskscheduler/time_calc_helper'
|
3
3
|
|
4
|
-
RSpec.describe
|
4
|
+
RSpec.describe Win32::TaskScheduler::TimeCalcHelper do
|
5
5
|
let(:object) { klass.new }
|
6
6
|
let(:klass) do
|
7
7
|
Class.new do
|
8
|
-
include
|
8
|
+
include Win32::TaskScheduler::TimeCalcHelper
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-taskscheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Park Heesob
|
@@ -117,17 +117,17 @@ files:
|
|
117
117
|
- examples/taskscheduler_example.rb
|
118
118
|
- lib/win32-taskscheduler.rb
|
119
119
|
- lib/win32/taskscheduler.rb
|
120
|
+
- lib/win32/taskscheduler/constants.rb
|
121
|
+
- lib/win32/taskscheduler/helper.rb
|
122
|
+
- lib/win32/taskscheduler/sid.rb
|
123
|
+
- lib/win32/taskscheduler/time_calc_helper.rb
|
120
124
|
- lib/win32/taskscheduler/version.rb
|
121
|
-
-
|
122
|
-
-
|
123
|
-
- lib/win32/windows/sid.rb
|
124
|
-
- lib/win32/windows/time_calc_helper.rb
|
125
|
-
- spec/functional/win32/taskschedular_spec.rb
|
126
|
-
- spec/functional/win32/windows/time_calc_helper_spec.rb
|
125
|
+
- spec/functional/win32/taskscheduler/time_calc_helper_spec.rb
|
126
|
+
- spec/functional/win32/taskscheduler_spec.rb
|
127
127
|
- spec/spec_helper.rb
|
128
|
-
- spec/unit/win32/
|
129
|
-
- spec/unit/win32/
|
130
|
-
- spec/unit/win32/
|
128
|
+
- spec/unit/win32/taskscheduler/constants_spec.rb
|
129
|
+
- spec/unit/win32/taskscheduler/time_calc_helper_spec.rb
|
130
|
+
- spec/unit/win32/taskscheduler_spec.rb
|
131
131
|
- test/test_taskscheduler.rb
|
132
132
|
- win32-taskscheduler.gemspec
|
133
133
|
homepage: http://github.com/chef/win32-taskscheduler
|
@@ -1,263 +0,0 @@
|
|
1
|
-
require_relative 'sid'
|
2
|
-
module Windows
|
3
|
-
module TaskSchedulerConstants
|
4
|
-
SERVICE_ACCOUNT_USERS = SID::SERVICE_ACCOUNT_USERS
|
5
|
-
|
6
|
-
BUILT_IN_GROUPS = SID::BUILT_IN_GROUPS
|
7
|
-
|
8
|
-
SYSTEM_USERS = SERVICE_ACCOUNT_USERS + BUILT_IN_GROUPS
|
9
|
-
|
10
|
-
# Triggers
|
11
|
-
|
12
|
-
# Trigger is set to run the task a single time
|
13
|
-
TASK_TIME_TRIGGER_ONCE = 1
|
14
|
-
|
15
|
-
# Trigger is set to run the task on a daily interval
|
16
|
-
TASK_TIME_TRIGGER_DAILY = 2
|
17
|
-
|
18
|
-
# Trigger is set to run the task on specific days of a specific week & month
|
19
|
-
TASK_TIME_TRIGGER_WEEKLY = 3
|
20
|
-
|
21
|
-
# Trigger is set to run the task on specific day(s) of the month
|
22
|
-
TASK_TIME_TRIGGER_MONTHLYDATE = 4
|
23
|
-
|
24
|
-
# Trigger is set to run the task on specific day(s) of the month
|
25
|
-
TASK_TIME_TRIGGER_MONTHLYDOW = 5
|
26
|
-
|
27
|
-
# Trigger is set to run the task if the system remains idle for the amount
|
28
|
-
# of time specified by the idle wait time of the task
|
29
|
-
TASK_EVENT_TRIGGER_ON_IDLE = 6
|
30
|
-
|
31
|
-
TASK_TRIGGER_REGISTRATION = 7
|
32
|
-
|
33
|
-
# Trigger is set to run the task at system startup
|
34
|
-
TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 8
|
35
|
-
|
36
|
-
# Trigger is set to run the task when a user logs on
|
37
|
-
TASK_EVENT_TRIGGER_AT_LOGON = 9
|
38
|
-
|
39
|
-
TASK_TRIGGER_SESSION_STATE_CHANGE = 11
|
40
|
-
|
41
|
-
# Daily Tasks
|
42
|
-
|
43
|
-
# The task will run on Sunday
|
44
|
-
TASK_SUNDAY = 0x1
|
45
|
-
|
46
|
-
# The task will run on Monday
|
47
|
-
TASK_MONDAY = 0x2
|
48
|
-
|
49
|
-
# The task will run on Tuesday
|
50
|
-
TASK_TUESDAY = 0x4
|
51
|
-
|
52
|
-
# The task will run on Wednesday
|
53
|
-
TASK_WEDNESDAY = 0x8
|
54
|
-
|
55
|
-
# The task will run on Thursday
|
56
|
-
TASK_THURSDAY = 0x10
|
57
|
-
|
58
|
-
# The task will run on Friday
|
59
|
-
TASK_FRIDAY = 0x20
|
60
|
-
|
61
|
-
# The task will run on Saturday
|
62
|
-
TASK_SATURDAY = 0x40
|
63
|
-
|
64
|
-
# Weekly tasks
|
65
|
-
|
66
|
-
# The task will run between the 1st and 7th day of the month
|
67
|
-
TASK_FIRST_WEEK = 0x01
|
68
|
-
|
69
|
-
# The task will run between the 8th and 14th day of the month
|
70
|
-
TASK_SECOND_WEEK = 0x02
|
71
|
-
|
72
|
-
# The task will run between the 15th and 21st day of the month
|
73
|
-
TASK_THIRD_WEEK = 0x04
|
74
|
-
|
75
|
-
# The task will run between the 22nd and 28th day of the month
|
76
|
-
TASK_FOURTH_WEEK = 0x08
|
77
|
-
|
78
|
-
# The task will run the last seven days of the month
|
79
|
-
TASK_LAST_WEEK = 0x10
|
80
|
-
|
81
|
-
# Monthly tasks
|
82
|
-
|
83
|
-
# The task will run in January
|
84
|
-
TASK_JANUARY = 0x1
|
85
|
-
|
86
|
-
# The task will run in February
|
87
|
-
TASK_FEBRUARY = 0x2
|
88
|
-
|
89
|
-
# The task will run in March
|
90
|
-
TASK_MARCH = 0x4
|
91
|
-
|
92
|
-
# The task will run in April
|
93
|
-
TASK_APRIL = 0x8
|
94
|
-
|
95
|
-
# The task will run in May
|
96
|
-
TASK_MAY = 0x10
|
97
|
-
|
98
|
-
# The task will run in June
|
99
|
-
TASK_JUNE = 0x20
|
100
|
-
|
101
|
-
# The task will run in July
|
102
|
-
TASK_JULY = 0x40
|
103
|
-
|
104
|
-
# The task will run in August
|
105
|
-
TASK_AUGUST = 0x80
|
106
|
-
|
107
|
-
# The task will run in September
|
108
|
-
TASK_SEPTEMBER = 0x100
|
109
|
-
|
110
|
-
# The task will run in October
|
111
|
-
TASK_OCTOBER = 0x200
|
112
|
-
|
113
|
-
# The task will run in November
|
114
|
-
TASK_NOVEMBER = 0x400
|
115
|
-
|
116
|
-
# The task will run in December
|
117
|
-
TASK_DECEMBER = 0x800
|
118
|
-
|
119
|
-
# Flags
|
120
|
-
|
121
|
-
# Used when converting AT service jobs into work items
|
122
|
-
TASK_FLAG_INTERACTIVE = 0x1
|
123
|
-
|
124
|
-
# The work item will be deleted when there are no more scheduled run times
|
125
|
-
TASK_FLAG_DELETE_WHEN_DONE = 0x2
|
126
|
-
|
127
|
-
# The work item is disabled. Useful for temporarily disabling a task
|
128
|
-
TASK_FLAG_DISABLED = 0x4
|
129
|
-
|
130
|
-
# The work item begins only if the computer is not in use at the scheduled
|
131
|
-
# start time
|
132
|
-
TASK_FLAG_START_ONLY_IF_IDLE = 0x10
|
133
|
-
|
134
|
-
# The work item terminates if the computer makes an idle to non-idle
|
135
|
-
# transition while the work item is running
|
136
|
-
TASK_FLAG_KILL_ON_IDLE_END = 0x20
|
137
|
-
|
138
|
-
# The work item does not start if the computer is running on battery power
|
139
|
-
TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
|
140
|
-
|
141
|
-
# The work item ends, and the associated application quits, if the computer
|
142
|
-
# switches to battery power
|
143
|
-
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
|
144
|
-
|
145
|
-
# The work item starts only if the computer is in a docking station
|
146
|
-
TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
|
147
|
-
|
148
|
-
# The work item created will be hidden
|
149
|
-
TASK_FLAG_HIDDEN = 0x200
|
150
|
-
|
151
|
-
# The work item runs only if there is a valid internet connection
|
152
|
-
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
|
153
|
-
|
154
|
-
# The work item starts again if the computer makes a non-idle to idle
|
155
|
-
# transition
|
156
|
-
TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
|
157
|
-
|
158
|
-
# The work item causes the system to be resumed, or awakened, if the
|
159
|
-
# system is running on batter power
|
160
|
-
TASK_FLAG_SYSTEM_REQUIRED = 0x1000
|
161
|
-
|
162
|
-
# The work item runs only if a specified account is logged on interactively
|
163
|
-
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
|
164
|
-
|
165
|
-
# Triggers
|
166
|
-
|
167
|
-
# The task will stop at some point in time
|
168
|
-
TASK_TRIGGER_FLAG_HAS_END_DATE = 0x1
|
169
|
-
|
170
|
-
# The task can be stopped at the end of the repetition period
|
171
|
-
TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0x2
|
172
|
-
|
173
|
-
# The task trigger is disabled
|
174
|
-
TASK_TRIGGER_FLAG_DISABLED = 0x4
|
175
|
-
|
176
|
-
# Run Level Types
|
177
|
-
# Tasks will be run with the least privileges
|
178
|
-
TASK_RUNLEVEL_LUA = 0
|
179
|
-
# Tasks will be run with the highest privileges
|
180
|
-
TASK_RUNLEVEL_HIGHEST = 1
|
181
|
-
|
182
|
-
# Logon Types
|
183
|
-
# Used for non-NT credentials
|
184
|
-
TASK_LOGON_NONE = 0
|
185
|
-
# Use a password for logging on the user
|
186
|
-
TASK_LOGON_PASSWORD = 1
|
187
|
-
# The service will log the user on using Service For User
|
188
|
-
TASK_LOGON_S4U = 2
|
189
|
-
# Task will be run only in an existing interactive session
|
190
|
-
TASK_LOGON_INTERACTIVE_TOKEN = 3
|
191
|
-
# Group activation. The groupId field specifies the group
|
192
|
-
TASK_LOGON_GROUP = 4
|
193
|
-
# When Local System, Local Service, or Network Service account is
|
194
|
-
# being used as a security context to run the task
|
195
|
-
TASK_LOGON_SERVICE_ACCOUNT = 5
|
196
|
-
# Not in use; currently identical to TASK_LOGON_PASSWORD
|
197
|
-
TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6
|
198
|
-
|
199
|
-
TASK_MAX_RUN_TIMES = 1440
|
200
|
-
TASKS_TO_RETRIEVE = 5
|
201
|
-
|
202
|
-
# Task creation
|
203
|
-
|
204
|
-
TASK_VALIDATE_ONLY = 0x1
|
205
|
-
TASK_CREATE = 0x2
|
206
|
-
TASK_UPDATE = 0x4
|
207
|
-
TASK_CREATE_OR_UPDATE = 0x6
|
208
|
-
TASK_DISABLE = 0x8
|
209
|
-
TASK_DONT_ADD_PRINCIPAL_ACE = 0x10
|
210
|
-
TASK_IGNORE_REGISTRATION_TRIGGERS = 0x20
|
211
|
-
|
212
|
-
# Priority classes
|
213
|
-
|
214
|
-
REALTIME_PRIORITY_CLASS = 0
|
215
|
-
HIGH_PRIORITY_CLASS = 1
|
216
|
-
ABOVE_NORMAL_PRIORITY_CLASS = 2 # Or 3
|
217
|
-
NORMAL_PRIORITY_CLASS = 4 # Or 5, 6
|
218
|
-
BELOW_NORMAL_PRIORITY_CLASS = 7 # Or 8
|
219
|
-
IDLE_PRIORITY_CLASS = 9 # Or 10
|
220
|
-
|
221
|
-
CLSCTX_INPROC_SERVER = 0x1
|
222
|
-
CLSID_CTask = [0x148BD520, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
223
|
-
CLSID_CTaskScheduler = [0x148BD52A, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
224
|
-
IID_ITaskScheduler = [0x148BD527, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
225
|
-
IID_ITask = [0x148BD524, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
226
|
-
IID_IPersistFile = [0x0000010b, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSC8')
|
227
|
-
|
228
|
-
# Days of month
|
229
|
-
|
230
|
-
TASK_FIRST = 0x01
|
231
|
-
TASK_SECOND = 0x02
|
232
|
-
TASK_THIRD = 0x04
|
233
|
-
TASK_FOURTH = 0x08
|
234
|
-
TASK_FIFTH = 0x10
|
235
|
-
TASK_SIXTH = 0x20
|
236
|
-
TASK_SEVENTH = 0x40
|
237
|
-
TASK_EIGHTH = 0x80
|
238
|
-
TASK_NINETH = 0x100
|
239
|
-
TASK_TENTH = 0x200
|
240
|
-
TASK_ELEVENTH = 0x400
|
241
|
-
TASK_TWELFTH = 0x800
|
242
|
-
TASK_THIRTEENTH = 0x1000
|
243
|
-
TASK_FOURTEENTH = 0x2000
|
244
|
-
TASK_FIFTEENTH = 0x4000
|
245
|
-
TASK_SIXTEENTH = 0x8000
|
246
|
-
TASK_SEVENTEENTH = 0x10000
|
247
|
-
TASK_EIGHTEENTH = 0x20000
|
248
|
-
TASK_NINETEENTH = 0x40000
|
249
|
-
TASK_TWENTIETH = 0x80000
|
250
|
-
TASK_TWENTY_FIRST = 0x100000
|
251
|
-
TASK_TWENTY_SECOND = 0x200000
|
252
|
-
TASK_TWENTY_THIRD = 0x400000
|
253
|
-
TASK_TWENTY_FOURTH = 0x800000
|
254
|
-
TASK_TWENTY_FIFTH = 0x1000000
|
255
|
-
TASK_TWENTY_SIXTH = 0x2000000
|
256
|
-
TASK_TWENTY_SEVENTH = 0x4000000
|
257
|
-
TASK_TWENTY_EIGHTH = 0x8000000
|
258
|
-
TASK_TWENTY_NINTH = 0x10000000
|
259
|
-
TASK_THIRTYETH = 0x20000000
|
260
|
-
TASK_THIRTY_FIRST = 0x40000000
|
261
|
-
TASK_LAST = 0x80000000
|
262
|
-
end
|
263
|
-
end
|
data/lib/win32/windows/helper.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
|
3
|
-
module Windows
|
4
|
-
module TaskSchedulerHelper
|
5
|
-
extend FFI::Library
|
6
|
-
|
7
|
-
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
|
8
|
-
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
|
9
|
-
FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF
|
10
|
-
|
11
|
-
ffi_lib :kernel32, :advapi32
|
12
|
-
|
13
|
-
attach_function :FormatMessage, :FormatMessageA,
|
14
|
-
[:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
|
15
|
-
|
16
|
-
attach_function :ConvertStringSidToSidW, [ :pointer, :pointer ], :bool
|
17
|
-
attach_function :LookupAccountSidW, [ :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer ], :bool
|
18
|
-
attach_function :LocalFree, [ :pointer ], :pointer
|
19
|
-
|
20
|
-
def win_error(function, err=FFI.errno)
|
21
|
-
err_msg = ''
|
22
|
-
flags = FORMAT_MESSAGE_IGNORE_INSERTS |
|
23
|
-
FORMAT_MESSAGE_FROM_SYSTEM |
|
24
|
-
FORMAT_MESSAGE_MAX_WIDTH_MASK
|
25
|
-
|
26
|
-
# 0x0409 == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
|
27
|
-
# We use English for errors because Ruby uses English for errors.
|
28
|
-
|
29
|
-
FFI::MemoryPointer.new(:char, 1024) do |buf|
|
30
|
-
len = FormatMessage(flags, nil, err , 0x0409, buf, buf.size, nil)
|
31
|
-
err_msg = function + ': ' + buf.read_string(len).strip
|
32
|
-
end
|
33
|
-
|
34
|
-
err_msg
|
35
|
-
end
|
36
|
-
|
37
|
-
def ole_error(function, err)
|
38
|
-
regex = /OLE error code:(.*?)\sin/
|
39
|
-
match = regex.match(err.to_s)
|
40
|
-
|
41
|
-
if match
|
42
|
-
error = match.captures.first.hex
|
43
|
-
win_error(function, error)
|
44
|
-
else
|
45
|
-
"#{function}: #{err.to_s}"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/lib/win32/windows/sid.rb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
require_relative 'helper'
|
2
|
-
|
3
|
-
module FFI
|
4
|
-
class Pointer
|
5
|
-
def read_wstring(num_wchars = nil)
|
6
|
-
if num_wchars.nil?
|
7
|
-
# Find the length of the string
|
8
|
-
length = 0
|
9
|
-
last_char = nil
|
10
|
-
while last_char != "\000\000"
|
11
|
-
length += 1
|
12
|
-
last_char = get_bytes(0, length * 2)[-2..-1]
|
13
|
-
end
|
14
|
-
|
15
|
-
num_wchars = length
|
16
|
-
end
|
17
|
-
|
18
|
-
wide_to_utf8(get_bytes(0, num_wchars * 2))
|
19
|
-
end
|
20
|
-
|
21
|
-
def wide_to_utf8(wstring)
|
22
|
-
# ensure it is actually UTF-16LE
|
23
|
-
# Ruby likes to mark binary data as ASCII-8BIT
|
24
|
-
wstring = wstring.force_encoding('UTF-16LE')
|
25
|
-
|
26
|
-
# encode it all as UTF-8 and remove trailing CRLF and NULL characters
|
27
|
-
wstring.encode('UTF-8').strip
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class SID
|
33
|
-
extend Windows::TaskSchedulerHelper
|
34
|
-
ERROR_INSUFFICIENT_BUFFER = 122
|
35
|
-
|
36
|
-
def self.LocalSystem
|
37
|
-
from_string_sid('S-1-5-18')
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.NtLocal
|
41
|
-
from_string_sid('S-1-5-19')
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.NtNetwork
|
45
|
-
from_string_sid('S-1-5-20')
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.BuiltinAdministrators
|
49
|
-
from_string_sid('S-1-5-32-544')
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.BuiltinUsers
|
53
|
-
from_string_sid('S-1-5-32-545')
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.Guests
|
57
|
-
from_string_sid('S-1-5-32-546')
|
58
|
-
end
|
59
|
-
|
60
|
-
# Converts a string-format security identifier (SID) into a valid, functional SID
|
61
|
-
# and returns a hash with account_name and account_simple_name
|
62
|
-
# @see https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsidtosidw
|
63
|
-
#
|
64
|
-
def self.from_string_sid(string_sid)
|
65
|
-
result = FFI::MemoryPointer.new :pointer
|
66
|
-
unless ConvertStringSidToSidW(utf8_to_wide(string_sid), result)
|
67
|
-
raise FFI::LastError.error
|
68
|
-
end
|
69
|
-
result_pointer = result.read_pointer
|
70
|
-
domain, name, use = account(result_pointer)
|
71
|
-
LocalFree(result_pointer)
|
72
|
-
account_names(domain, name, use)
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.utf8_to_wide(ustring)
|
76
|
-
# ensure it is actually UTF-8
|
77
|
-
# Ruby likes to mark binary data as ASCII-8BIT
|
78
|
-
ustring = (ustring + '').force_encoding('UTF-8')
|
79
|
-
|
80
|
-
# ensure we have the double-null termination Windows Wide likes
|
81
|
-
ustring += "\000\000" if ustring.empty? || ustring[-1].chr != "\000"
|
82
|
-
|
83
|
-
# encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
|
84
|
-
ustring.encode('UTF-16LE')
|
85
|
-
end
|
86
|
-
|
87
|
-
# Accepts a security identifier (SID) as input.
|
88
|
-
# It retrieves the name of the account for this SID and the name of the
|
89
|
-
# first domain on which this SID is found
|
90
|
-
# @see https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-lookupaccountsidw
|
91
|
-
#
|
92
|
-
def self.account(sid)
|
93
|
-
sid = sid.pointer if sid.respond_to?(:pointer)
|
94
|
-
name_size = FFI::Buffer.new(:long).write_long(0)
|
95
|
-
referenced_domain_name_size = FFI::Buffer.new(:long).write_long(0)
|
96
|
-
|
97
|
-
if LookupAccountSidW(nil, sid, nil, name_size, nil, referenced_domain_name_size, nil)
|
98
|
-
raise 'Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountSid, and got no error!'
|
99
|
-
elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
|
100
|
-
raise FFI::LastError.error
|
101
|
-
end
|
102
|
-
|
103
|
-
name = FFI::MemoryPointer.new :char, (name_size.read_long * 2)
|
104
|
-
referenced_domain_name = FFI::MemoryPointer.new :char, (referenced_domain_name_size.read_long * 2)
|
105
|
-
use = FFI::Buffer.new(:long).write_long(0)
|
106
|
-
unless LookupAccountSidW(nil, sid, name, name_size, referenced_domain_name, referenced_domain_name_size, use)
|
107
|
-
raise FFI::LastError.error
|
108
|
-
end
|
109
|
-
[referenced_domain_name.read_wstring(referenced_domain_name_size.read_long), name.read_wstring(name_size.read_long), use.read_long]
|
110
|
-
end
|
111
|
-
|
112
|
-
# Formats domain, name and returns a hash with
|
113
|
-
# account_name and account_simple_name
|
114
|
-
#
|
115
|
-
def self.account_names(domain, name, _use)
|
116
|
-
account_name = !domain.to_s.empty? ? "#{domain}\\#{name}" : name
|
117
|
-
account_simple_name = name
|
118
|
-
{ account_name: account_name, account_simple_name: account_simple_name }
|
119
|
-
end
|
120
|
-
|
121
|
-
SERVICE_ACCOUNT_USERS = [self.LocalSystem, self.NtLocal, self.NtNetwork].map do |user|
|
122
|
-
[user[:account_simple_name].upcase, user[:account_name].upcase]
|
123
|
-
end.flatten.freeze
|
124
|
-
|
125
|
-
BUILT_IN_GROUPS = [self.BuiltinAdministrators, self.BuiltinUsers, self.Guests].map do |user|
|
126
|
-
[user[:account_simple_name].upcase, user[:account_name].upcase]
|
127
|
-
end.flatten.freeze
|
128
|
-
end
|
@@ -1,130 +0,0 @@
|
|
1
|
-
module Windows
|
2
|
-
module TimeCalcHelper
|
3
|
-
|
4
|
-
# Returns actual no of days for given month;
|
5
|
-
# Array with a 0 is defined to give actual result without
|
6
|
-
# any manipulation. eg, DAYS_IN_A_MONTH[1] = 31
|
7
|
-
# 0(NUMBER) is kept to avoid exceptions during calculations
|
8
|
-
DAYS_IN_A_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
9
|
-
|
10
|
-
# Returns no of days in a given month of a year
|
11
|
-
def days_in_month(month, year)
|
12
|
-
(month == 2 && is_leap_year?(year)) ? 29 : DAYS_IN_A_MONTH[month]
|
13
|
-
end
|
14
|
-
|
15
|
-
# Year is leap when it is a multiple of 4 and not a multiple of 100.
|
16
|
-
# But it can be a multiple of 400
|
17
|
-
def is_leap_year?(year)
|
18
|
-
(((year % 4).zero? && !(year % 100).zero?) || (year % 400).zero?)
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns total time duration in minutes
|
22
|
-
def time_in_minutes(time_str)
|
23
|
-
time_in_seconds(time_str) / 60
|
24
|
-
end
|
25
|
-
|
26
|
-
# Calculates total time duration in seconds
|
27
|
-
def time_in_seconds(time_str)
|
28
|
-
dt_tm_hash = time_details(time_str)
|
29
|
-
curr_time = Time.now
|
30
|
-
|
31
|
-
# Basic time variables
|
32
|
-
future_year = curr_time.year + dt_tm_hash[:year].to_i
|
33
|
-
future_month = curr_time.month + dt_tm_hash[:month].to_i
|
34
|
-
future_day = curr_time.day + dt_tm_hash[:day].to_i
|
35
|
-
future_hr = curr_time.hour + dt_tm_hash[:hour].to_i
|
36
|
-
future_min = curr_time.min + dt_tm_hash[:min].to_i
|
37
|
-
future_sec = curr_time.sec + dt_tm_hash[:sec].to_i
|
38
|
-
|
39
|
-
# 'extra value' calculations for these time variables
|
40
|
-
future_sec, future_min = extra_time(future_sec, future_min, 60)
|
41
|
-
future_min, future_hr = extra_time(future_min, future_hr, 60)
|
42
|
-
future_hr, future_day = extra_time(future_hr, future_day, 24)
|
43
|
-
|
44
|
-
# explicit method to calculate overloaded days;
|
45
|
-
# They may stretch upto years; heance leap year & months are into consideration
|
46
|
-
future_day, future_month, future_year = extra_days(future_day, future_month, future_year, curr_time.month, curr_time.year)
|
47
|
-
|
48
|
-
future_month, future_year = extra_months(future_month, future_year, curr_time.month, curr_time.year)
|
49
|
-
|
50
|
-
future_time = Time.new(future_year, future_month, future_day, future_hr, future_min, future_sec)
|
51
|
-
|
52
|
-
# Difference in time will return seconds
|
53
|
-
future_time.to_i - curr_time.to_i
|
54
|
-
end
|
55
|
-
|
56
|
-
# a will contain extra value of low_rank (in high_rank(eg min));
|
57
|
-
# b will hold actual low_rank value(ie sec) Example:
|
58
|
-
# low_rank = 65, high_rank = 2, div_val = 60
|
59
|
-
# Hence a = 1; b = 5
|
60
|
-
def extra_time(low_rank, high_rank, div_val)
|
61
|
-
a, b = low_rank.divmod(div_val)
|
62
|
-
high_rank += a; low_rank = b
|
63
|
-
[low_rank, high_rank]
|
64
|
-
end
|
65
|
-
|
66
|
-
def extra_months(month_count, year_count, init_month, init_year)
|
67
|
-
year, month_count = month_count.divmod(12)
|
68
|
-
if year.positive? && month_count.zero?
|
69
|
-
month_count = 12
|
70
|
-
year -= 1
|
71
|
-
end
|
72
|
-
year_count += year
|
73
|
-
[month_count, year_count]
|
74
|
-
end
|
75
|
-
|
76
|
-
# Returns no of actual days with all overloaded months & Years
|
77
|
-
def extra_days(days_count, month_count, year_count, init_month, init_year)
|
78
|
-
# Will keep increamenting them with surplus days
|
79
|
-
days = days_count
|
80
|
-
mth = init_month
|
81
|
-
yr = init_year
|
82
|
-
|
83
|
-
loop do
|
84
|
-
days -= days_in_month(mth, yr)
|
85
|
-
break if days <= 0
|
86
|
-
mth += 1
|
87
|
-
if mth > 12
|
88
|
-
mth = 1; yr += 1
|
89
|
-
end
|
90
|
-
days_count = days
|
91
|
-
end
|
92
|
-
|
93
|
-
# Setting actual incremented values
|
94
|
-
month_count += (mth - init_month)
|
95
|
-
year_count += (yr - init_year)
|
96
|
-
|
97
|
-
[days_count, month_count, year_count]
|
98
|
-
end
|
99
|
-
|
100
|
-
# Extracts "P_Y_M_DT_H_M_S" format and
|
101
|
-
# Returns a hash with applicable values of
|
102
|
-
# (keys =>) [:year, :month, :day, :hour, :min, :sec]
|
103
|
-
# Example: "PT3S" => {sec: 3}
|
104
|
-
def time_details(time_str)
|
105
|
-
tm_detail = {}
|
106
|
-
if time_str.to_s != ''
|
107
|
-
# time_str will be like "PxxYxxMxxDTxxHxxMxxS"
|
108
|
-
# Ignoring 'P' and extracting date and time
|
109
|
-
dt, tm = time_str[1..-1].split('T')
|
110
|
-
|
111
|
-
# Replacing strings
|
112
|
-
if dt.to_s != ''
|
113
|
-
dt['Y'] = 'year' if dt['Y']; dt['M'] = 'month' if dt['M']; dt['D'] = 'day' if dt['D']
|
114
|
-
dt_tm_array_to_hash(dt, tm_detail)
|
115
|
-
end
|
116
|
-
|
117
|
-
if tm.to_s != ''
|
118
|
-
tm['H'] = 'hour' if tm['H']; tm['M'] = 'min' if tm['M']; tm['S'] = 'sec' if tm['S']
|
119
|
-
dt_tm_array_to_hash(tm, tm_detail)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
tm_detail
|
123
|
-
end
|
124
|
-
|
125
|
-
# Method to convert date/time array to hash
|
126
|
-
def dt_tm_array_to_hash(arr, tm_detail)
|
127
|
-
arr.split(/(\d+)/)[1..-1].each_slice(2).inject(tm_detail) { |h, i| h[i.last.to_sym] = i.first; h }
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|