win32-service 0.8.0 → 0.8.1

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 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