win32-service 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7a9f9dd48b778b4dbe8d17581a339f8a6a15069e
4
+ data.tar.gz: cd6fff85777b0440d645f9714462a6ab6e0fd2b3
5
+ SHA512:
6
+ metadata.gz: 709dedda4dee8f0b78985723b48ed5a059c183337c16ea2fd8145b4a8eedf4fb28561e8f2bc0d0317e1ac78b2b9cbc4b72ca0cdf6897dfa7cd045195b84e3868
7
+ data.tar.gz: c3024db96b9db1493eca72ecfe5f02730cacdf0c22c29482ee5bed062edb667e34e556d2694c541c4e98a8776d6c284da1ff3d47267a6dd9ab8ea78bd7c306d6
data/CHANGES CHANGED
@@ -1,4 +1,14 @@
1
- == 0.8.0 - ???
1
+ == 0.8.1 - 2-Aug-2013
2
+ * Fixed the failure actions code for the Service#configure method
3
+ so that it is also using FFI.
4
+ * An internal fix for the Daemon class was added. In short, you no longer
5
+ need to, nor should you, call exit! in a service_stop method. Thanks go
6
+ to Adrian Candaten for pointing out the issue in general.
7
+ * Made FFI functions private.
8
+ * Added Rake as a development dependency.
9
+ * Removed an internal helper function that was no longer being used.
10
+
11
+ == 0.8.0 - 19-Jun-2013
2
12
  * Converted all code to use FFI. This includes the Daemon code, which means
3
13
  that it also now works with JRuby.
4
14
 
data/README CHANGED
@@ -1,75 +1,75 @@
1
- == Description
2
- The win32-service library allows you to control or create MS Windows services.
3
-
4
- == Installation
5
- gem install win32-service
6
-
7
- == Synopsis
8
- require 'win32/service'
9
-
10
- # Iterate over the available services
11
- Service.services do |service|
12
- p service
13
- end
14
-
15
- == More Documentation
16
- Please see the documentation in the 'doc' directory, or the gem documentation
17
- that was installed when you installed this library as a gem.
18
-
19
- == Known Issues
20
- === Problem:
21
- Service.delete causes "Unable to delete: The specified service has been
22
- marked for deletion."
23
-
24
- === Troubleshooting:
25
- This can be caused by one of two things. Either you attempted to delete a
26
- running service without stopping it first, or you have the Services
27
- Administrative Tool (GUI) open. In the former case, the solution is to first
28
- stop the service if it's running. In the latter, close the Services GUI
29
- admin tool before deleting.
30
-
31
- === Problem:
32
- Service.start causes, "The service did not respond to the start or control
33
- request in a timely fashion."
34
-
35
- === Troubleshooting:
36
- The best way to debug your services is to wrap your entire Daemon subclass
37
- in a begin/end block and send error messages to a file. That should give a
38
- good clue as to the nature of the problem. The most probable culprits are:
39
-
40
- * You've tried to require a library that's not in your $LOAD_PATH. Make sure
41
- that your require statements are inside the begin/rescue block so that you can
42
- easily find those mistakes.
43
-
44
- * Your have a bad binary path name. Be sure to use an absolute path name for
45
- the binary path name, including the full path to the Ruby interpreter, e.g.
46
- 'c:\ruby\bin\ruby' instead of just 'ruby'.
47
-
48
- * You've got a syntax error in your code somewhere.
49
-
50
- == See Also
51
- ruby-wmi
52
-
53
- == Future Plans
54
- Add service_session_change hook
55
-
56
- == Copyright
57
- (C) 2003-2013, Daniel J. Berger, All Rights Reserved
58
-
59
- == License
60
- Artistic 2.0
61
-
62
- == Contributions
63
- Although this library is free, please consider having your company
64
- setup a gittip if used by your company professionally.
65
-
66
- http://www.gittip.com/djberg96/
67
-
68
- == Warranty
69
- This package is provided "as is" and without any express or
70
- implied warranties, including, without limitation, the implied
71
- warranties of merchantability and fitness for a particular purpose.
72
-
73
- == Authors
74
- Daniel J. Berger
75
- Park Heesob
1
+ == Description
2
+ The win32-service library allows you to control or create MS Windows services.
3
+
4
+ == Installation
5
+ gem install win32-service
6
+
7
+ == Synopsis
8
+ require 'win32/service'
9
+
10
+ # Iterate over the available services
11
+ Service.services do |service|
12
+ p service
13
+ end
14
+
15
+ == More Documentation
16
+ Please see the documentation in the 'doc' directory, or the gem documentation
17
+ that was installed when you installed this library as a gem.
18
+
19
+ == Known Issues
20
+ === Problem:
21
+ Service.delete causes "Unable to delete: The specified service has been
22
+ marked for deletion."
23
+
24
+ === Troubleshooting:
25
+ This can be caused by one of two things. Either you attempted to delete a
26
+ running service without stopping it first, or you have the Services
27
+ Administrative Tool (GUI) open. In the former case, the solution is to first
28
+ stop the service if it's running. In the latter, close the Services GUI
29
+ admin tool before deleting.
30
+
31
+ === Problem:
32
+ Service.start causes, "The service did not respond to the start or control
33
+ request in a timely fashion."
34
+
35
+ === Troubleshooting:
36
+ The best way to debug your services is to wrap your entire Daemon subclass
37
+ in a begin/end block and send error messages to a file. That should give a
38
+ good clue as to the nature of the problem. The most probable culprits are:
39
+
40
+ * You've tried to require a library that's not in your $LOAD_PATH. Make sure
41
+ that your require statements are inside the begin/rescue block so that you can
42
+ easily find those mistakes.
43
+
44
+ * Your have a bad binary path name. Be sure to use an absolute path name for
45
+ the binary path name, including the full path to the Ruby interpreter, e.g.
46
+ 'c:\ruby\bin\ruby' instead of just 'ruby'.
47
+
48
+ * You've got a syntax error in your code somewhere.
49
+
50
+ == See Also
51
+ ruby-wmi
52
+
53
+ == Future Plans
54
+ Add service_session_change hook
55
+
56
+ == Copyright
57
+ (C) 2003-2013, Daniel J. Berger, All Rights Reserved
58
+
59
+ == License
60
+ Artistic 2.0
61
+
62
+ == Contributions
63
+ Although this library is free, please consider having your company
64
+ setup a gittip if used by your company professionally.
65
+
66
+ http://www.gittip.com/djberg96/
67
+
68
+ == Warranty
69
+ This package is provided "as is" and without any express or
70
+ implied warranties, including, without limitation, the implied
71
+ warranties of merchantability and fitness for a particular purpose.
72
+
73
+ == Authors
74
+ Daniel J. Berger
75
+ Park Heesob
data/Rakefile CHANGED
@@ -13,7 +13,12 @@ namespace 'gem' do
13
13
  desc "Create the win32-service gem"
14
14
  task :create => [:clean] do
15
15
  spec = eval(IO.read('win32-service.gemspec'))
16
- Gem::Builder.new(spec).build
16
+ if Gem::VERSION.to_f < 2.0
17
+ Gem::Builder.new(spec).build
18
+ else
19
+ require 'rubygems/package'
20
+ Gem::Package.build(spec)
21
+ end
17
22
  end
18
23
 
19
24
  desc "Install the win32-service gem"
@@ -58,15 +58,15 @@ begin
58
58
  File.open(LOG_FILE, 'a'){ |f| f.puts msg }
59
59
  end
60
60
 
61
- # This event triggers when the service receives a signal to stop. I've
62
- # added an explicit "exit!" here to ensure that the Ruby interpreter exits
63
- # properly. I use 'exit!' instead of 'exit' because otherwise Ruby will
64
- # raise a SystemExitError, which I don't want.
61
+ # This event triggers when the service receives a signal to stop.
62
+ #
63
+ # NOTE: Older versions of this code used an explicit exit! call
64
+ # to force the Ruby interpreter to exit. Don't do that. It is no
65
+ # longer required and, in fact, may cause issues.
65
66
  #
66
67
  def service_stop
67
68
  msg = 'Received stop signal at: ' + Time.now.to_s
68
69
  File.open(LOG_FILE, 'a'){ |f| f.puts msg }
69
- exit!
70
70
  end
71
71
 
72
72
  # This event triggers when the service receives a signal to pause.
@@ -1,30 +1,30 @@
1
- #######################################################################
2
- # demo_services.rb
3
- #
4
- # Test script for general futzing that shows off the basic
5
- # capabilities of this library. Modify as you see fit.
6
- #
7
- # You can run this sample program via the "example:services" task.
8
- #######################################################################
9
- require 'win32/service'
10
- include Win32
11
-
12
- puts "VERSION: " + Service::VERSION
13
-
14
- p Service.exists?('Schedule')
15
- p Service.exists?('bogusxxx')
16
-
17
- status = Service.status('Schedule')
18
- p status
19
-
20
- info = Service.config_info('Schedule')
21
-
22
- print "\n\nShowing config info for Schedule service\n\n"
23
- p info
24
-
25
- print "\n\nAbout to show all services\n\n"
26
- sleep 10
27
-
28
- Service.services{ |struct|
29
- p struct
30
- }
1
+ #######################################################################
2
+ # demo_services.rb
3
+ #
4
+ # Test script for general futzing that shows off the basic
5
+ # capabilities of this library. Modify as you see fit.
6
+ #
7
+ # You can run this sample program via the "example:services" task.
8
+ #######################################################################
9
+ require 'win32/service'
10
+ include Win32
11
+
12
+ puts "VERSION: " + Service::VERSION
13
+
14
+ p Service.exists?('Schedule')
15
+ p Service.exists?('bogusxxx')
16
+
17
+ status = Service.status('Schedule')
18
+ p status
19
+
20
+ info = Service.config_info('Schedule')
21
+
22
+ print "\n\nShowing config info for Schedule service\n\n"
23
+ p info
24
+
25
+ print "\n\nAbout to show all services\n\n"
26
+ sleep 10
27
+
28
+ Service.services{ |struct|
29
+ p struct
30
+ }
data/lib/win32/daemon.rb CHANGED
@@ -18,7 +18,7 @@ module Win32
18
18
  extend Windows::Functions
19
19
 
20
20
  # The version of this library
21
- VERSION = '0.8.0'
21
+ VERSION = '0.8.1'
22
22
 
23
23
  private
24
24
 
@@ -294,6 +294,7 @@ module Win32
294
294
  end
295
295
 
296
296
  service_stop() if respond_to?('service_stop')
297
+ SetEvent(@@hStopCompletedEvent)
297
298
  end
298
299
 
299
300
  if respond_to?('service_main')
data/lib/win32/service.rb CHANGED
@@ -17,7 +17,7 @@ module Win32
17
17
  extend Windows::Functions
18
18
 
19
19
  # The version of the win32-service library
20
- VERSION = '0.8.0'
20
+ VERSION = '0.8.1'
21
21
 
22
22
  # SCM security and access rights
23
23
 
@@ -1180,7 +1180,7 @@ module Win32
1180
1180
  #
1181
1181
  def self.configure_failure_actions(handle_scs, opts)
1182
1182
  if opts['failure_actions']
1183
- token_handle = 0.chr * 4
1183
+ token_handle = FFI::MemoryPointer.new(:ulong)
1184
1184
 
1185
1185
  bool = OpenProcessToken(
1186
1186
  GetCurrentProcess(),
@@ -1189,28 +1189,34 @@ module Win32
1189
1189
  )
1190
1190
 
1191
1191
  unless bool
1192
- error = get_last_error
1192
+ error = FFI.errno
1193
1193
  CloseServiceHandle(handle_scs)
1194
- raise Error, error
1194
+ raise SystemCallError.new('OpenProcessToken', error)
1195
1195
  end
1196
1196
 
1197
- token_handle = token_handle.unpack('L').first
1197
+ token_handle = token_handle.read_ulong
1198
1198
 
1199
1199
  # Get the LUID for shutdown privilege.
1200
- luid = 0.chr * 8
1200
+ luid = LUID.new
1201
1201
 
1202
1202
  unless LookupPrivilegeValue('', 'SeShutdownPrivilege', luid)
1203
- error = get_last_error
1203
+ error = FFI.errno
1204
1204
  CloseServiceHandle(handle_scs)
1205
- raise Error, error
1205
+ raise SystemCallError.new('LookupPrivilegeValue', error)
1206
1206
  end
1207
1207
 
1208
- tkp = [1].pack('L') + luid + [SE_PRIVILEGE_ENABLED].pack('L')
1208
+ luid_and_attrs = LUID_AND_ATTRIBUTES.new
1209
+ luid_and_attrs[:Luid] = luid
1210
+ luid_and_attrs[:Attributes] = SE_PRIVILEGE_ENABLED
1211
+
1212
+ tkp = TOKEN_PRIVILEGES.new
1213
+ tkp[:PrivilegeCount] = 1
1214
+ tkp[:Privileges][0] = luid_and_attrs
1209
1215
 
1210
1216
  # Enable shutdown privilege in access token of this process
1211
1217
  bool = AdjustTokenPrivileges(
1212
1218
  token_handle,
1213
- 0,
1219
+ false,
1214
1220
  tkp,
1215
1221
  tkp.size,
1216
1222
  nil,
@@ -1218,78 +1224,56 @@ module Win32
1218
1224
  )
1219
1225
 
1220
1226
  unless bool
1221
- error = get_last_error
1227
+ error = FFI.errno
1222
1228
  CloseServiceHandle(handle_scs)
1223
- raise Error, error
1229
+ raise SystemCallError.new('AdjustTokenPrivileges', error)
1224
1230
  end
1225
1231
  end
1226
1232
 
1227
- fail_buf = 0.chr * 20 # sizeof(SERVICE_FAILURE_ACTIONS)
1233
+ sfa = SERVICE_FAILURE_ACTIONS.new
1228
1234
 
1229
1235
  if opts['failure_reset_period']
1230
- fail_buf[0,4] = [opts['failure_reset_period']].pack('L')
1236
+ sfa[:dwResetPeriod] = opts['failure_reset_period']
1231
1237
  end
1232
1238
 
1233
1239
  if opts['failure_reboot_message']
1234
- fail_buf[4,4] = [opts['failure_reboot_message']].pack('p*')
1240
+ sfa[:lpRebootMsg] = FFI::MemoryPointer.from_string(opts['failure_reboot_message'])
1235
1241
  end
1236
1242
 
1237
1243
  if opts['failure_command']
1238
- fail_buf[8,4] = [opts['failure_command']].pack('p*')
1244
+ sfa[:lpCommand] = FFI::MemoryPointer.from_string(opts['failure_command'])
1239
1245
  end
1240
1246
 
1241
1247
  if opts['failure_actions']
1242
- actions = []
1248
+ action_size = opts['failure_actions'].size
1249
+ action_ptr = FFI::MemoryPointer.new(SC_ACTION, action_size)
1243
1250
 
1244
- opts['failure_actions'].each{ |action|
1245
- action_buf = 0.chr * 8
1246
- action_buf[0, 4] = [action].pack('L')
1247
- action_buf[4, 4] = [opts['failure_delay']].pack('L')
1248
- actions << action_buf
1249
- }
1251
+ actions = action_size.times.collect do |i|
1252
+ SC_ACTION.new(action_ptr + i * SC_ACTION.size)
1253
+ end
1250
1254
 
1251
- actions = actions.join
1255
+ opts['failure_actions'].each_with_index{ |action, i|
1256
+ actions[i][:Type] = action
1257
+ actions[i][:Delay] = opts['failure_delay']
1258
+ }
1252
1259
 
1253
- fail_buf[12,4] = [opts['failure_actions'].length].pack('L')
1254
- fail_buf[16,4] = [actions].pack('p*')
1260
+ sfa[:cActions] = action_size
1261
+ sfa[:lpsaActions] = action_ptr
1255
1262
  end
1256
1263
 
1257
1264
  bool = ChangeServiceConfig2(
1258
1265
  handle_scs,
1259
1266
  SERVICE_CONFIG_FAILURE_ACTIONS,
1260
- fail_buf
1267
+ sfa
1261
1268
  )
1262
1269
 
1263
1270
  unless bool
1264
- error = get_last_error
1271
+ error = FFI.errno
1265
1272
  CloseServiceHandle(handle_scs)
1266
- raise Error, error
1273
+ raise SystemCallError.new('ChangeServiceConfig2', error)
1267
1274
  end
1268
1275
  end
1269
1276
 
1270
- # Unravels a pointer to an array of dependencies. Takes the address
1271
- # that points the array as an argument.
1272
- #
1273
- def self.get_dependencies(address)
1274
- dep_buf = ""
1275
-
1276
- while address != 0
1277
- char_buf = 0.chr
1278
- memcpy(char_buf, address, 1)
1279
- address += 1
1280
- dep_buf += char_buf
1281
- break if dep_buf[-2,2] == "\0\0"
1282
- end
1283
-
1284
- dependencies = []
1285
-
1286
- if dep_buf != "\0\0"
1287
- dependencies = dep_buf.split("\000\000").first.split(0.chr)
1288
- end
1289
-
1290
- dependencies
1291
- end
1292
-
1293
1277
  # Returns a human readable string indicating the action type.
1294
1278
  #
1295
1279
  def self.get_action_type(action_type)
@@ -127,6 +127,10 @@ module Windows
127
127
 
128
128
  NO_ERROR = 0
129
129
 
130
+ SE_PRIVILEGE_ENABLED = 0x00000002
131
+ TOKEN_ADJUST_PRIVILEGES = 0x0020
132
+ TOKEN_QUERY = 0x0008
133
+
130
134
  # Errors
131
135
 
132
136
  ERROR_INSUFFICIENT_BUFFER = 122
@@ -4,6 +4,14 @@ module Windows
4
4
  module Functions
5
5
  extend FFI::Library
6
6
 
7
+ # Make FFI functions private
8
+ module FFI::Library
9
+ def attach_pfunc(*args)
10
+ attach_function(*args)
11
+ private args[0]
12
+ end
13
+ end
14
+
7
15
  typedef :ulong, :dword
8
16
  typedef :uintptr_t, :handle
9
17
  typedef :pointer, :ptr
@@ -13,51 +21,55 @@ module Windows
13
21
 
14
22
  ffi_lib :kernel32
15
23
 
16
- attach_function :CloseHandle, [:handle], :bool
17
- attach_function :CreateEvent, :CreateEventA, [:ptr, :bool, :bool, :str], :handle
18
- attach_function :CreateThread, [:ptr, :size_t, :ptr, :ptr, :dword, :ptr], :handle, :blocking => true
19
- attach_function :EnterCriticalSection, [:ptr], :void
20
- attach_function :FormatMessage, :FormatMessageA, [:ulong, :ptr, :ulong, :ulong, :str, :ulong, :ptr], :ulong
21
- attach_function :InitializeCriticalSection, [:ptr], :void
22
- attach_function :LeaveCriticalSection, [:ptr], :void
23
- attach_function :SetEvent, [:handle], :bool
24
- attach_function :WaitForSingleObject, [:handle, :dword], :dword, :blocking => true
25
- attach_function :WaitForMultipleObjects, [:dword, :ptr, :bool, :dword], :dword
24
+ attach_pfunc :CloseHandle, [:handle], :bool
25
+ attach_pfunc :CreateEvent, :CreateEventA, [:ptr, :bool, :bool, :str], :handle
26
+ attach_pfunc :CreateThread, [:ptr, :size_t, :ptr, :ptr, :dword, :ptr], :handle, :blocking => true
27
+ attach_pfunc :EnterCriticalSection, [:ptr], :void
28
+ attach_pfunc :FormatMessage, :FormatMessageA, [:ulong, :ptr, :ulong, :ulong, :str, :ulong, :ptr], :ulong
29
+ attach_pfunc :GetCurrentProcess, [], :handle
30
+ attach_pfunc :InitializeCriticalSection, [:ptr], :void
31
+ attach_pfunc :LeaveCriticalSection, [:ptr], :void
32
+ attach_pfunc :SetEvent, [:handle], :bool
33
+ attach_pfunc :WaitForSingleObject, [:handle, :dword], :dword, :blocking => true
34
+ attach_pfunc :WaitForMultipleObjects, [:dword, :ptr, :bool, :dword], :dword
26
35
 
27
36
  ffi_lib :advapi32
28
37
 
29
38
  callback :handler_ex, [:ulong, :ulong, :ptr, :ptr], :void
30
39
 
31
- attach_function :CloseServiceHandle, [:handle], :bool
40
+ attach_pfunc :AdjustTokenPrivileges, [:handle, :bool, :ptr, :dword, :ptr, :ptr], :bool
41
+ attach_pfunc :CloseServiceHandle, [:handle], :bool
32
42
 
33
- attach_function :ChangeServiceConfig, :ChangeServiceConfigA,
43
+ attach_pfunc :ChangeServiceConfig, :ChangeServiceConfigA,
34
44
  [:handle, :dword, :dword, :dword, :str, :str, :ptr, :str, :str, :str, :str],
35
45
  :bool
36
46
 
37
- attach_function :ChangeServiceConfig2, :ChangeServiceConfig2A, [:handle, :dword, :ptr], :bool
47
+ attach_pfunc :ChangeServiceConfig2, :ChangeServiceConfig2A, [:handle, :dword, :ptr], :bool
38
48
 
39
- attach_function :CreateService, :CreateServiceA,
49
+ attach_pfunc :CreateService, :CreateServiceA,
40
50
  [:handle, :string, :string, :dword, :dword, :dword, :dword,
41
51
  :string, :string, :ptr, :pointer, :string, :string],
42
52
  :handle
43
53
 
44
- attach_function :ControlService, [:handle, :dword, :ptr], :bool
45
- attach_function :DeleteService, [:handle], :bool
54
+ attach_pfunc :ControlService, [:handle, :dword, :ptr], :bool
55
+ attach_pfunc :DeleteService, [:handle], :bool
46
56
 
47
- attach_function :EnumServicesStatusEx, :EnumServicesStatusExA,
57
+ attach_pfunc :EnumServicesStatusEx, :EnumServicesStatusExA,
48
58
  [:handle, :int, :dword, :dword, :ptr, :dword, :ptr, :ptr, :ptr, :string],
49
59
  :bool
50
60
 
51
- attach_function :GetServiceDisplayName, :GetServiceDisplayNameA, [:handle, :string, :ptr, :ptr], :bool
52
- attach_function :GetServiceKeyName, :GetServiceKeyNameA, [:handle, :string, :ptr, :ptr], :bool
53
- attach_function :OpenSCManager, :OpenSCManagerA, [:ptr, :ptr, :dword], :handle
54
- attach_function :OpenService, :OpenServiceA, [:handle, :string, :dword], :handle
55
- attach_function :QueryServiceConfig, :QueryServiceConfigA, [:handle, :ptr, :dword, :ptr], :bool
56
- attach_function :QueryServiceConfig2, :QueryServiceConfig2A, [:handle, :dword, :ptr, :dword, :ptr], :bool
57
- attach_function :QueryServiceStatusEx, [:handle, :int, :ptr, :dword, :ptr], :bool
58
- attach_function :RegisterServiceCtrlHandlerEx, :RegisterServiceCtrlHandlerExA, [:str, :handler_ex, :ptr], :handle
59
- attach_function :SetServiceStatus, [:handle, :ptr], :bool
60
- attach_function :StartService, :StartServiceA, [:handle, :dword, :ptr], :bool
61
- attach_function :StartServiceCtrlDispatcher, :StartServiceCtrlDispatcherA, [:ptr], :bool, :blocking => true
61
+ attach_pfunc :GetServiceDisplayName, :GetServiceDisplayNameA, [:handle, :string, :ptr, :ptr], :bool
62
+ attach_pfunc :GetServiceKeyName, :GetServiceKeyNameA, [:handle, :string, :ptr, :ptr], :bool
63
+ attach_pfunc :LookupPrivilegeValue, :LookupPrivilegeValueA, [:string, :string, :ptr], :bool
64
+ attach_pfunc :OpenSCManager, :OpenSCManagerA, [:ptr, :ptr, :dword], :handle
65
+ attach_pfunc :OpenProcessToken, [:handle, :dword, :ptr], :bool
66
+ attach_pfunc :OpenService, :OpenServiceA, [:handle, :string, :dword], :handle
67
+ attach_pfunc :QueryServiceConfig, :QueryServiceConfigA, [:handle, :ptr, :dword, :ptr], :bool
68
+ attach_pfunc :QueryServiceConfig2, :QueryServiceConfig2A, [:handle, :dword, :ptr, :dword, :ptr], :bool
69
+ attach_pfunc :QueryServiceStatusEx, [:handle, :int, :ptr, :dword, :ptr], :bool
70
+ attach_pfunc :RegisterServiceCtrlHandlerEx, :RegisterServiceCtrlHandlerExA, [:str, :handler_ex, :ptr], :handle
71
+ attach_pfunc :SetServiceStatus, [:handle, :ptr], :bool
72
+ attach_pfunc :StartService, :StartServiceA, [:handle, :dword, :ptr], :bool
73
+ attach_pfunc :StartServiceCtrlDispatcher, :StartServiceCtrlDispatcherA, [:ptr], :bool, :blocking => true
62
74
  end
63
75
  end
@@ -92,5 +92,26 @@ module Windows
92
92
  :lpServiceProc, :pointer
93
93
  )
94
94
  end
95
+
96
+ class LUID < FFI::Struct
97
+ layout(
98
+ :LowPart, :ulong,
99
+ :HighPart, :long
100
+ )
101
+ end
102
+
103
+ class LUID_AND_ATTRIBUTES < FFI::Struct
104
+ layout(
105
+ :Luid, LUID,
106
+ :Attributes, :ulong
107
+ )
108
+ end
109
+
110
+ class TOKEN_PRIVILEGES < FFI::Struct
111
+ layout(
112
+ :PrivilegeCount, :dword,
113
+ :Privileges, [LUID_AND_ATTRIBUTES, 1]
114
+ )
115
+ end
95
116
  end
96
117
  end
@@ -17,7 +17,7 @@ class TC_Daemon < Test::Unit::TestCase
17
17
  end
18
18
 
19
19
  test "version number is set properly" do
20
- assert_equal('0.8.0', Daemon::VERSION)
20
+ assert_equal('0.8.1', Daemon::VERSION)
21
21
  end
22
22
 
23
23
  test "constructor basic functionality" do
@@ -4,8 +4,8 @@
4
4
  # Tests for the Win32::Service class.
5
5
  ##########################################################################
6
6
  require 'test-unit'
7
- require 'win32/service'
8
7
  require 'win32/security'
8
+ require 'win32/service'
9
9
  require 'socket'
10
10
 
11
11
  class TC_Win32_Service < Test::Unit::TestCase
@@ -20,6 +20,9 @@ class TC_Win32_Service < Test::Unit::TestCase
20
20
  @service_stat = nil
21
21
  @services = []
22
22
  @elevated = Win32::Security.elevated_security?
23
+
24
+ @singleton_methods = Win32::Service.methods.map{ |m| m.to_s }
25
+ @instance_methods = Win32::Service.instance_methods.map{ |m| m.to_s }
23
26
  end
24
27
 
25
28
  def start_service(service)
@@ -49,7 +52,7 @@ class TC_Win32_Service < Test::Unit::TestCase
49
52
  end
50
53
 
51
54
  test "version number is expected value" do
52
- assert_equal('0.8.0', Win32::Service::VERSION)
55
+ assert_equal('0.8.1', Win32::Service::VERSION)
53
56
  end
54
57
 
55
58
  test "services basic functionality" do
@@ -392,6 +395,18 @@ class TC_Win32_Service < Test::Unit::TestCase
392
395
  assert_not_nil(Win32::Service::STOPPED)
393
396
  end
394
397
 
398
+ test "internal ffi functions are not public as singleton methods" do
399
+ assert_false(@singleton_methods.include?('CloseHandle'))
400
+ assert_false(@singleton_methods.include?('ControlService'))
401
+ assert_false(@singleton_methods.include?('DeleteService'))
402
+ end
403
+
404
+ test "internal ffi functions are not public as instance methods" do
405
+ assert_false(@instance_methods.include?('CloseHandle'))
406
+ assert_false(@instance_methods.include?('ControlService'))
407
+ assert_false(@instance_methods.include?('DeleteService'))
408
+ end
409
+
395
410
  def teardown
396
411
  @display_name = nil
397
412
  @service_name = nil
@@ -1,129 +1,129 @@
1
- ########################################################################
2
- # test_win32_service_create.rb
3
- #
4
- # Test case for the Service.create method. This test case will create
5
- # a dummy (notepad) service. It won't actually run of course.
6
- ########################################################################
7
- require 'test-unit'
8
- require 'win32/service'
9
-
10
- class TC_Win32_Service_Create < Test::Unit::TestCase
11
- def self.startup
12
- @@service1 = "notepad_service1"
13
- @@service2 = "notepad_service2"
14
- @@command = "C:\\windows\\system32\\notepad.exe"
15
-
16
- Win32::Service.new(
17
- :service_name => @@service1,
18
- :binary_path_name => @@command
19
- )
20
-
21
- Win32::Service.new(
22
- :service_name => @@service2,
23
- :display_name => 'Notepad Test',
24
- :desired_access => Win32::Service::ALL_ACCESS,
25
- :service_type => Win32::Service::WIN32_OWN_PROCESS,
26
- :start_type => Win32::Service::DISABLED,
27
- :error_control => Win32::Service::ERROR_IGNORE,
28
- :binary_path_name => @@command,
29
- :load_order_group => 'Network',
30
- :dependencies => 'W32Time',
31
- :description => 'Test service. Please delete me'
32
- )
33
- end
34
-
35
- def setup
36
- @info1 = Win32::Service.config_info(@@service1)
37
- @info2 = Win32::Service.config_info(@@service2)
38
- end
39
-
40
- test "constructor basic functionality" do
41
- assert_respond_to(Win32::Service, :new)
42
- end
43
-
44
- # Service.create works, but the test fails. Might be a bug in test-unit.
45
- test "create is an alias for new" do
46
- assert_respond_to(Win32::Service, :create)
47
- #assert_alias_method(Win32::Service, :create, :new)
48
- end
49
-
50
- test "ensure services were created in startup method" do
51
- notify "If this test fails then remaining results are meaningless."
52
- assert_true(Win32::Service.exists?(@@service1))
53
- assert_true(Win32::Service.exists?(@@service2))
54
- end
55
-
56
- test "expected service type configuration information" do
57
- assert_equal('own process, interactive', @info1.service_type)
58
- end
59
-
60
- test "expected start type configuration information" do
61
- assert_equal('demand start', @info1.start_type)
62
- end
63
-
64
- test "expected error control configuration information" do
65
- assert_equal('normal', @info1.error_control)
66
- end
67
-
68
- test "expected binary path name configuration information" do
69
- assert_equal(@@command, @info1.binary_path_name)
70
- end
71
-
72
- test "expected load order group configuration information" do
73
- assert_equal('', @info1.load_order_group)
74
- end
75
-
76
- test "expected tag id configuration information" do
77
- assert_equal(0, @info1.tag_id)
78
- end
79
-
80
- test "expected dependency configuration information" do
81
- assert_equal([], @info1.dependencies)
82
- end
83
-
84
- test "expected service start time configuration information" do
85
- assert_equal('LocalSystem', @info1.service_start_name)
86
- end
87
-
88
- test "expected display name configuration information" do
89
- assert_equal('notepad_service1', @info1.display_name)
90
- end
91
-
92
- test "configuration information options are set properly for service 2" do
93
- assert_equal('own process', @info2.service_type)
94
- assert_equal('disabled', @info2.start_type)
95
- assert_equal('ignore', @info2.error_control)
96
- assert_equal(@@command, @info2.binary_path_name)
97
- assert_equal('Network', @info2.load_order_group)
98
- assert_equal(0, @info2.tag_id)
99
- assert_equal(['W32Time'], @info2.dependencies)
100
- assert_equal('LocalSystem', @info2.service_start_name)
101
- assert_equal('Notepad Test', @info2.display_name)
102
- end
103
-
104
- test "at least one argument is required or an error is raised" do
105
- assert_raise(ArgumentError){ Win32::Service.new }
106
- end
107
-
108
- test "passing a bogus option to the constructor will cause an error" do
109
- assert_raise(ArgumentError){ Win32::Service.new(:bogus => 'test.exe') }
110
- end
111
-
112
- test "the service name must be provided or an error is raised" do
113
- assert_raise(ArgumentError){ Win32::Service.new(:binary_path_name => 'test.exe') }
114
- end
115
-
116
- def teardown
117
- @info1 = nil
118
- @info2 = nil
119
- end
120
-
121
- def self.shutdown
122
- Win32::Service.delete(@@service1) if Win32::Service.exists?(@@service1)
123
- Win32::Service.delete(@@service2) if Win32::Service.exists?(@@service2)
124
-
125
- @@service1 = nil
126
- @@service2 = nil
127
- @@command = nil
128
- end
129
- end
1
+ ########################################################################
2
+ # test_win32_service_create.rb
3
+ #
4
+ # Test case for the Service.create method. This test case will create
5
+ # a dummy (notepad) service. It won't actually run of course.
6
+ ########################################################################
7
+ require 'test-unit'
8
+ require 'win32/service'
9
+
10
+ class TC_Win32_Service_Create < Test::Unit::TestCase
11
+ def self.startup
12
+ @@service1 = "notepad_service1"
13
+ @@service2 = "notepad_service2"
14
+ @@command = "C:\\windows\\system32\\notepad.exe"
15
+
16
+ Win32::Service.new(
17
+ :service_name => @@service1,
18
+ :binary_path_name => @@command
19
+ )
20
+
21
+ Win32::Service.new(
22
+ :service_name => @@service2,
23
+ :display_name => 'Notepad Test',
24
+ :desired_access => Win32::Service::ALL_ACCESS,
25
+ :service_type => Win32::Service::WIN32_OWN_PROCESS,
26
+ :start_type => Win32::Service::DISABLED,
27
+ :error_control => Win32::Service::ERROR_IGNORE,
28
+ :binary_path_name => @@command,
29
+ :load_order_group => 'Network',
30
+ :dependencies => 'W32Time',
31
+ :description => 'Test service. Please delete me'
32
+ )
33
+ end
34
+
35
+ def setup
36
+ @info1 = Win32::Service.config_info(@@service1)
37
+ @info2 = Win32::Service.config_info(@@service2)
38
+ end
39
+
40
+ test "constructor basic functionality" do
41
+ assert_respond_to(Win32::Service, :new)
42
+ end
43
+
44
+ # Service.create works, but the test fails. Might be a bug in test-unit.
45
+ test "create is an alias for new" do
46
+ assert_respond_to(Win32::Service, :create)
47
+ #assert_alias_method(Win32::Service, :create, :new)
48
+ end
49
+
50
+ test "ensure services were created in startup method" do
51
+ notify "If this test fails then remaining results are meaningless."
52
+ assert_true(Win32::Service.exists?(@@service1))
53
+ assert_true(Win32::Service.exists?(@@service2))
54
+ end
55
+
56
+ test "expected service type configuration information" do
57
+ assert_equal('own process, interactive', @info1.service_type)
58
+ end
59
+
60
+ test "expected start type configuration information" do
61
+ assert_equal('demand start', @info1.start_type)
62
+ end
63
+
64
+ test "expected error control configuration information" do
65
+ assert_equal('normal', @info1.error_control)
66
+ end
67
+
68
+ test "expected binary path name configuration information" do
69
+ assert_equal(@@command, @info1.binary_path_name)
70
+ end
71
+
72
+ test "expected load order group configuration information" do
73
+ assert_equal('', @info1.load_order_group)
74
+ end
75
+
76
+ test "expected tag id configuration information" do
77
+ assert_equal(0, @info1.tag_id)
78
+ end
79
+
80
+ test "expected dependency configuration information" do
81
+ assert_equal([], @info1.dependencies)
82
+ end
83
+
84
+ test "expected service start time configuration information" do
85
+ assert_equal('LocalSystem', @info1.service_start_name)
86
+ end
87
+
88
+ test "expected display name configuration information" do
89
+ assert_equal('notepad_service1', @info1.display_name)
90
+ end
91
+
92
+ test "configuration information options are set properly for service 2" do
93
+ assert_equal('own process', @info2.service_type)
94
+ assert_equal('disabled', @info2.start_type)
95
+ assert_equal('ignore', @info2.error_control)
96
+ assert_equal(@@command, @info2.binary_path_name)
97
+ assert_equal('Network', @info2.load_order_group)
98
+ assert_equal(0, @info2.tag_id)
99
+ assert_equal(['W32Time'], @info2.dependencies)
100
+ assert_equal('LocalSystem', @info2.service_start_name)
101
+ assert_equal('Notepad Test', @info2.display_name)
102
+ end
103
+
104
+ test "at least one argument is required or an error is raised" do
105
+ assert_raise(ArgumentError){ Win32::Service.new }
106
+ end
107
+
108
+ test "passing a bogus option to the constructor will cause an error" do
109
+ assert_raise(ArgumentError){ Win32::Service.new(:bogus => 'test.exe') }
110
+ end
111
+
112
+ test "the service name must be provided or an error is raised" do
113
+ assert_raise(ArgumentError){ Win32::Service.new(:binary_path_name => 'test.exe') }
114
+ end
115
+
116
+ def teardown
117
+ @info1 = nil
118
+ @info2 = nil
119
+ end
120
+
121
+ def self.shutdown
122
+ Win32::Service.delete(@@service1) if Win32::Service.exists?(@@service1)
123
+ Win32::Service.delete(@@service2) if Win32::Service.exists?(@@service2)
124
+
125
+ @@service1 = nil
126
+ @@service2 = nil
127
+ @@command = nil
128
+ end
129
+ end
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'win32-service'
5
- spec.version = '0.8.0'
5
+ spec.version = '0.8.1'
6
6
  spec.authors = ['Daniel J. Berger', 'Park Heesob']
7
7
  spec.license = 'Artistic 2.0'
8
8
  spec.email = 'djberg96@gmail.com'
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_dependency('ffi')
24
24
  spec.add_development_dependency('test-unit', '>= 2.4.0')
25
+ spec.add_development_dependency('rake')
25
26
 
26
27
  spec.description = <<-EOF
27
28
  The win32-service library provides a Ruby interface to services on
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
5
- prerelease:
4
+ version: 0.8.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Daniel J. Berger
@@ -10,44 +9,57 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2013-06-20 00:00:00.000000000 Z
12
+ date: 2013-08-02 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: ffi
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
- - - ! '>='
18
+ - - '>='
21
19
  - !ruby/object:Gem::Version
22
20
  version: '0'
23
21
  type: :runtime
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
- - - ! '>='
25
+ - - '>='
29
26
  - !ruby/object:Gem::Version
30
27
  version: '0'
31
28
  - !ruby/object:Gem::Dependency
32
29
  name: test-unit
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
- - - ! '>='
32
+ - - '>='
37
33
  - !ruby/object:Gem::Version
38
34
  version: 2.4.0
39
35
  type: :development
40
36
  prerelease: false
41
37
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
38
  requirements:
44
- - - ! '>='
39
+ - - '>='
45
40
  - !ruby/object:Gem::Version
46
41
  version: 2.4.0
47
- description: ! " The win32-service library provides a Ruby interface to services
48
- on\n MS Windows. You can create new services, or control, configure and\n inspect
49
- existing services.\n\n In addition, you can create a pure Ruby service by using
50
- the Daemon\n class that is included as part of the library.\n"
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: |2
57
+ The win32-service library provides a Ruby interface to services on
58
+ MS Windows. You can create new services, or control, configure and
59
+ inspect existing services.
60
+
61
+ In addition, you can create a pure Ruby service by using the Daemon
62
+ class that is included as part of the library.
51
63
  email: djberg96@gmail.com
52
64
  executables: []
53
65
  extensions: []
@@ -83,27 +95,26 @@ files:
83
95
  homepage: http://github.com/djberg96/win32-service
84
96
  licenses:
85
97
  - Artistic 2.0
98
+ metadata: {}
86
99
  post_install_message:
87
100
  rdoc_options: []
88
101
  require_paths:
89
102
  - lib
90
103
  required_ruby_version: !ruby/object:Gem::Requirement
91
- none: false
92
104
  requirements:
93
- - - ! '>='
105
+ - - '>='
94
106
  - !ruby/object:Gem::Version
95
107
  version: '0'
96
108
  required_rubygems_version: !ruby/object:Gem::Requirement
97
- none: false
98
109
  requirements:
99
- - - ! '>='
110
+ - - '>='
100
111
  - !ruby/object:Gem::Version
101
112
  version: '0'
102
113
  requirements: []
103
114
  rubyforge_project:
104
- rubygems_version: 1.8.24
115
+ rubygems_version: 2.0.3
105
116
  signing_key:
106
- specification_version: 3
117
+ specification_version: 4
107
118
  summary: An interface for MS Windows services
108
119
  test_files:
109
120
  - test/test_win32_daemon.rb