win32-service 0.6.1 → 0.7.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.
- data/CHANGES +21 -0
- data/MANIFEST +6 -5
- data/README +2 -1
- data/Rakefile +128 -0
- data/doc/daemon.txt +5 -3
- data/doc/service.txt +13 -10
- data/examples/demo_daemon_ctl.rb +122 -0
- data/examples/demo_services.rb +23 -0
- data/ext/extconf.rb +5 -5
- data/ext/win32/daemon.c +107 -107
- data/lib/win32/daemon.so +0 -0
- data/lib/win32/service.rb +52 -24
- data/test/{tc_daemon.rb → test_win32_daemon.rb} +4 -1
- data/test/{tc_service.rb → test_win32_service.rb} +5 -14
- data/test/test_win32_service_configure.rb +86 -0
- data/test/test_win32_service_create.rb +103 -0
- data/test/{tc_service_info.rb → test_win32_service_info.rb} +12 -3
- data/test/{tc_service_status.rb → test_win32_service_status.rb} +3 -0
- data/win32-service.gemspec +49 -0
- metadata +42 -21
- data/test/tc_service_create.rb +0 -83
data/CHANGES
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
== 0.7.0 - 30-Jun-2009
|
2
|
+
* INTERFACE CHANGE: The Service.new and Service.configure methods now accept
|
3
|
+
a single hash argument only. The :service_name key is mandatory, while the
|
4
|
+
:host key is optional.
|
5
|
+
* Fixed a bug in the Service.services method where a segfault would occur if
|
6
|
+
the binary_path_name was greater than 260 characters. The buffer size has
|
7
|
+
been increased to 1024 characters. Thanks go to Dan Daniels for the spot.
|
8
|
+
* Altered the Daemon class so that the Ruby interpreter was not invoked within
|
9
|
+
the Service_Main function. The functions that close stdin, stdout and stderr
|
10
|
+
are now called within the mainloop method. Thanks go to Tim Hanson for the
|
11
|
+
spot and the patch.
|
12
|
+
* Eliminated a minor build warning in daemon.c
|
13
|
+
* License changed to Artistic 2.0, with corresponding gemspec update.
|
14
|
+
* Added an uninstall rake task.
|
15
|
+
* Updated the service_stop method in the demo_daemon.rb file, and added some
|
16
|
+
additional comments.
|
17
|
+
* The test files were renamed.
|
18
|
+
* Several tests were updated and refactored to use Test::Unit 2.x. That
|
19
|
+
library is now a dependency.
|
20
|
+
* The 'uninstall' Rakefile task is now more verbose.
|
21
|
+
|
1
22
|
== 0.6.1 - 1-Jan-2008
|
2
23
|
* The Service.services method now handles the scenario where a particular
|
3
24
|
service may still exist, but its underlying registry entry has been deleted.
|
data/MANIFEST
CHANGED
@@ -11,8 +11,9 @@
|
|
11
11
|
* examples/demo_daemon_ctl.rb
|
12
12
|
* examples/demo_services.rb
|
13
13
|
* lib/win32/service.rb
|
14
|
-
* test/
|
15
|
-
* test/
|
16
|
-
* test/
|
17
|
-
* test/
|
18
|
-
* test/
|
14
|
+
* test/test_win32_daemon.rb
|
15
|
+
* test/test_win32_service_configure.rb
|
16
|
+
* test/test_win32_service_create.rb
|
17
|
+
* test/test_win32_service_info.rb
|
18
|
+
* test/test_win32_service_status.rb
|
19
|
+
* test/test_win32_service.rb
|
data/README
CHANGED
data/Rakefile
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rbconfig'
|
5
|
+
include Config
|
6
|
+
|
7
|
+
desc "Cleans up the C related files created during the build"
|
8
|
+
task :clean do
|
9
|
+
Dir.chdir('ext') do
|
10
|
+
if File.exists?('daemon.o') || File.exists?('daemon.so')
|
11
|
+
sh 'nmake distclean'
|
12
|
+
end
|
13
|
+
File.delete('win32/daemon.so') if File.exists?('win32/daemon.so')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Builds, but does not install, the win32-service library"
|
18
|
+
task :build => [:clean] do
|
19
|
+
Dir.chdir('ext') do
|
20
|
+
ruby 'extconf.rb'
|
21
|
+
sh 'nmake'
|
22
|
+
FileUtils.cp('daemon.so', 'win32/daemon.so')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Install the win32-service library (non-gem)"
|
27
|
+
task :install => [:build] do
|
28
|
+
Dir.chdir('ext') do
|
29
|
+
sh 'nmake install'
|
30
|
+
end
|
31
|
+
install_dir = File.join(CONFIG['sitelibdir'], 'win32')
|
32
|
+
Dir.mkdir(install_dir) unless File.exists?(install_dir)
|
33
|
+
FileUtils.cp('lib/win32/service.rb', install_dir, :verbose => true)
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Install the win32-service library as a gem'
|
37
|
+
task :install_gem => [:clean] do
|
38
|
+
ruby 'win32-service.gemspec'
|
39
|
+
file = Dir['win32-service*.gem'].first
|
40
|
+
sh "gem install #{file}"
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'Uninstall the (non-gem) win32-service library.'
|
44
|
+
task :uninstall do
|
45
|
+
service = File.join(CONFIG['sitelibdir'], 'win32', 'service.rb')
|
46
|
+
FileUtils.rm(service, :verbose => true) if File.exists?(service)
|
47
|
+
|
48
|
+
daemon = File.join(CONFIG['sitearchdir'], 'win32', 'daemon.so')
|
49
|
+
FileUtils.rm(daemon, :verbose => true) if File.exists?(daemon)
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Build a binary gem"
|
53
|
+
task :build_binary_gem => [:build] do
|
54
|
+
if File.exists?('lib/win32/daemon.rb')
|
55
|
+
mv 'lib/win32/daemon.rb', 'lib/win32/daemon.orig'
|
56
|
+
end
|
57
|
+
|
58
|
+
cp 'ext/win32/daemon.so', 'lib/win32/daemon.so'
|
59
|
+
|
60
|
+
task :build_binary_gem => [:clean]
|
61
|
+
|
62
|
+
spec = Gem::Specification.new do |gem|
|
63
|
+
gem.name = 'win32-service'
|
64
|
+
gem.version = '0.7.0'
|
65
|
+
gem.authors = ['Daniel J. Berger', 'Park Heesob']
|
66
|
+
gem.email = 'djberg96@gmail.com'
|
67
|
+
gem.license = 'Artistic 2.0'
|
68
|
+
gem.homepage = 'http://www.rubyforge.org/projects/win32utils'
|
69
|
+
gem.platform = Gem::Platform::CURRENT
|
70
|
+
gem.summary = 'An interface for MS Windows services'
|
71
|
+
gem.test_files = Dir['test/test*.rb']
|
72
|
+
gem.has_rdoc = true
|
73
|
+
gem.files = Dir['**/*'].delete_if{ |f|
|
74
|
+
f.include?('CVS') || f.include?('ext') || f.include?('daemon.orig')
|
75
|
+
}
|
76
|
+
|
77
|
+
gem.extra_rdoc_files = [
|
78
|
+
'CHANGES',
|
79
|
+
'README',
|
80
|
+
'MANIFEST',
|
81
|
+
'doc/service.txt',
|
82
|
+
'doc/daemon.txt',
|
83
|
+
'ext/win32/daemon.c'
|
84
|
+
]
|
85
|
+
|
86
|
+
gem.rubyforge_project = 'win32utils'
|
87
|
+
gem.required_ruby_version = '>= 1.8.2'
|
88
|
+
|
89
|
+
gem.add_dependency('windows-pr', '>= 1.0.5')
|
90
|
+
gem.add_dependency('test-unit', '>= 2.0.2')
|
91
|
+
|
92
|
+
gem.description = <<-EOF
|
93
|
+
The win32-service library provides a Ruby interface to services on
|
94
|
+
MS Windows. You can create new services, or control, configure and
|
95
|
+
inspect existing services.
|
96
|
+
|
97
|
+
In addition, you can create a pure Ruby service by using the Daemon
|
98
|
+
class that is included as part of the library.
|
99
|
+
EOF
|
100
|
+
end
|
101
|
+
|
102
|
+
Gem::Builder.new(spec).build
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "Run the test suite for the win32-service library"
|
106
|
+
Rake::TestTask.new do |t|
|
107
|
+
task :test => :build
|
108
|
+
t.libs << 'ext'
|
109
|
+
t.verbose = true
|
110
|
+
t.warning = true
|
111
|
+
end
|
112
|
+
|
113
|
+
desc "Run the test suite for the Win32::Daemon class (only)"
|
114
|
+
Rake::TestTask.new('test_daemon') do |t|
|
115
|
+
task :test_daemon => :build
|
116
|
+
t.libs << 'ext'
|
117
|
+
t.verbose = true
|
118
|
+
t.warning = true
|
119
|
+
t.test_files = FileList['test/test_win32_daemon.rb']
|
120
|
+
end
|
121
|
+
|
122
|
+
task :test do
|
123
|
+
Rake.application[:clean].execute
|
124
|
+
end
|
125
|
+
|
126
|
+
task :test_daemon do
|
127
|
+
Rake.application[:clean].execute
|
128
|
+
end
|
data/doc/daemon.txt
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
def service_main
|
8
8
|
while running?
|
9
9
|
sleep 3
|
10
|
-
File.open("c:\\test.log","a
|
10
|
+
File.open("c:\\test.log", "a"){ |f| f.puts "service is running" }
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -131,6 +131,8 @@ Daemon::IDLE
|
|
131
131
|
http://rubyforge.org/projects/win32utils.
|
132
132
|
|
133
133
|
= Future Plans
|
134
|
+
Rewrite the Daemon class in pure Ruby.
|
135
|
+
|
134
136
|
Suggestions welcome. Please log them on the Feature Request tracker at
|
135
137
|
http://rubyforge.org/projects/win32utils
|
136
138
|
|
@@ -140,10 +142,10 @@ Daemon::IDLE
|
|
140
142
|
service responsiveness issues.
|
141
143
|
|
142
144
|
= Copyright
|
143
|
-
(C) 2003-
|
145
|
+
(C) 2003-2009 Daniel J. Berger, All Rights Reserved
|
144
146
|
|
145
147
|
= License
|
146
|
-
|
148
|
+
Artistic 2.0
|
147
149
|
|
148
150
|
= Warranty
|
149
151
|
This package is provided "as is" and without any express or
|
data/doc/service.txt
CHANGED
@@ -17,7 +17,8 @@
|
|
17
17
|
include Win32
|
18
18
|
|
19
19
|
# Create a new service
|
20
|
-
Service.create(
|
20
|
+
Service.create(
|
21
|
+
:service_name => 'some_service',
|
21
22
|
:service_type => Service::WIN32_OWN_PROCESS,
|
22
23
|
:description => 'A custom service I wrote just for fun'
|
23
24
|
:start_type => Service::AUTO_START,
|
@@ -52,11 +53,12 @@
|
|
52
53
|
}
|
53
54
|
|
54
55
|
= Class Methods
|
55
|
-
Service.new(
|
56
|
-
Creates a new service
|
57
|
-
if no host is specified. The +options+ parameter is a hash that can
|
56
|
+
Service.new(options={})
|
57
|
+
Creates a new service. The +options+ parameter is a hash that can
|
58
58
|
contain any of the following parameters, and their default values:
|
59
59
|
|
60
|
+
* service_name => nil (mandatory)
|
61
|
+
* host => nil
|
60
62
|
* display_name => service_name
|
61
63
|
* desired_access => Service::ALL_ACCESS
|
62
64
|
* service_type => Service::WIN32_OWN_PROCESS | Service::INTERACTIVE_PROCESS
|
@@ -69,11 +71,12 @@ Service.new(service_name, host=nil, options={})
|
|
69
71
|
* password => nil
|
70
72
|
* description => nil
|
71
73
|
|
72
|
-
Service.configure(
|
73
|
-
Configures
|
74
|
-
|
75
|
-
of the following parameters:
|
74
|
+
Service.configure(options={})
|
75
|
+
Configures an existing service. The +options+ parameter is a hash that can
|
76
|
+
contain any of the following parameters:
|
76
77
|
|
78
|
+
* service_name (mandatory)
|
79
|
+
* host
|
77
80
|
* service_type
|
78
81
|
* start_type
|
79
82
|
* error_control
|
@@ -350,10 +353,10 @@ Service::ERROR_CRITICAL
|
|
350
353
|
Use RegisterServiceCtrlHandlerEx().
|
351
354
|
|
352
355
|
= Copyright
|
353
|
-
(C) 2003-
|
356
|
+
(C) 2003-2009, Daniel J. Berger, All Rights Reserved
|
354
357
|
|
355
358
|
= License
|
356
|
-
|
359
|
+
Artistic 2.0
|
357
360
|
|
358
361
|
= Warranty
|
359
362
|
This package is provided "as is" and without any express or
|
@@ -0,0 +1,122 @@
|
|
1
|
+
############################################################################
|
2
|
+
# demo_daemon_ctl.rb
|
3
|
+
#
|
4
|
+
# This is a command line script for installing and/or running a small
|
5
|
+
# Ruby program as a service. The service will simply write a small bit
|
6
|
+
# of text to a file every 20 seconds. It will also write some text to the
|
7
|
+
# file during the initialization (service_init) step.
|
8
|
+
#
|
9
|
+
# It should take about 10 seconds to start, which is intentional - it's a test
|
10
|
+
# of the service_init hook, so don't be surprised if you see "one moment,
|
11
|
+
# start pending" about 10 times on the command line.
|
12
|
+
#
|
13
|
+
# The file in question is C:\test.log. Feel free to delete it when finished.
|
14
|
+
#
|
15
|
+
# To run the service, you must install it first.
|
16
|
+
#
|
17
|
+
# Usage: ruby demo_daemon_ctl.rb <option>
|
18
|
+
#
|
19
|
+
# Note that you *must* pass this program an option
|
20
|
+
#
|
21
|
+
# Options:
|
22
|
+
# install - Installs the service. The service name is "DemoSvc"
|
23
|
+
# and the display name is "Demo".
|
24
|
+
# start - Starts the service. Make sure you stop it at some point or
|
25
|
+
# you will eventually fill up your filesystem!.
|
26
|
+
# stop - Stops the service.
|
27
|
+
# pause - Pauses the service.
|
28
|
+
# resume - Resumes the service.
|
29
|
+
# uninstall - Uninstalls the service.
|
30
|
+
# delete - Same as uninstall.
|
31
|
+
#
|
32
|
+
# You can also used the Windows Services GUI to start and stop the service.
|
33
|
+
#
|
34
|
+
# To get to the Windows Services GUI just follow:
|
35
|
+
# Start -> Control Panel -> Administrative Tools -> Services
|
36
|
+
############################################################################
|
37
|
+
require 'win32/service'
|
38
|
+
require 'rbconfig'
|
39
|
+
include Win32
|
40
|
+
include Config
|
41
|
+
|
42
|
+
# Make sure you're using the version you think you're using.
|
43
|
+
puts 'VERSION: ' + Service::VERSION
|
44
|
+
|
45
|
+
SERVICE_NAME = 'DemoSvc'
|
46
|
+
SERVICE_DISPLAYNAME = 'Demo'
|
47
|
+
|
48
|
+
# Quote the full path to deal with possible spaces in the path name.
|
49
|
+
ruby = File.join(CONFIG['bindir'], 'ruby').tr('/', '\\')
|
50
|
+
path = ' "' + File.dirname(File.expand_path($0)).tr('/', '\\')
|
51
|
+
path += '\demo_daemon.rb"'
|
52
|
+
cmd = ruby + path
|
53
|
+
|
54
|
+
# You must provide at least one argument.
|
55
|
+
raise ArgumentError, 'No argument provided' unless ARGV[0]
|
56
|
+
|
57
|
+
case ARGV[0].downcase
|
58
|
+
when 'install'
|
59
|
+
Service.new(
|
60
|
+
:service_name => SERVICE_NAME,
|
61
|
+
:display_name => SERVICE_DISPLAYNAME,
|
62
|
+
:description => 'Sample Ruby service',
|
63
|
+
:binary_path_name => cmd
|
64
|
+
)
|
65
|
+
puts 'Service ' + SERVICE_NAME + ' installed'
|
66
|
+
when 'start'
|
67
|
+
if Service.status(SERVICE_NAME).current_state != 'running'
|
68
|
+
Service.start(SERVICE_NAME, nil, 'hello', 'world')
|
69
|
+
while Service.status(SERVICE_NAME).current_state != 'running'
|
70
|
+
puts 'One moment...' + Service.status(SERVICE_NAME).current_state
|
71
|
+
sleep 1
|
72
|
+
end
|
73
|
+
puts 'Service ' + SERVICE_NAME + ' started'
|
74
|
+
else
|
75
|
+
puts 'Already running'
|
76
|
+
end
|
77
|
+
when 'stop'
|
78
|
+
if Service.status(SERVICE_NAME).current_state != 'stopped'
|
79
|
+
Service.stop(SERVICE_NAME)
|
80
|
+
while Service.status(SERVICE_NAME).current_state != 'stopped'
|
81
|
+
puts 'One moment...' + Service.status(SERVICE_NAME).current_state
|
82
|
+
sleep 1
|
83
|
+
end
|
84
|
+
puts 'Service ' + SERVICE_NAME + ' stopped'
|
85
|
+
else
|
86
|
+
puts 'Already stopped'
|
87
|
+
end
|
88
|
+
when 'uninstall', 'delete'
|
89
|
+
if Service.status(SERVICE_NAME).current_state != 'stopped'
|
90
|
+
Service.stop(SERVICE_NAME)
|
91
|
+
end
|
92
|
+
while Service.status(SERVICE_NAME).current_state != 'stopped'
|
93
|
+
puts 'One moment...' + Service.status(SERVICE_NAME).current_state
|
94
|
+
sleep 1
|
95
|
+
end
|
96
|
+
Service.delete(SERVICE_NAME)
|
97
|
+
puts 'Service ' + SERVICE_NAME + ' deleted'
|
98
|
+
when 'pause'
|
99
|
+
if Service.status(SERVICE_NAME).current_state != 'paused'
|
100
|
+
Service.pause(SERVICE_NAME)
|
101
|
+
while Service.status(SERVICE_NAME).current_state != 'paused'
|
102
|
+
puts 'One moment...' + Service.status(SERVICE_NAME).current_state
|
103
|
+
sleep 1
|
104
|
+
end
|
105
|
+
puts 'Service ' + SERVICE_NAME + ' paused'
|
106
|
+
else
|
107
|
+
puts 'Already paused'
|
108
|
+
end
|
109
|
+
when 'resume'
|
110
|
+
if Service.status(SERVICE_NAME).current_state != 'running'
|
111
|
+
Service.resume(SERVICE_NAME)
|
112
|
+
while Service.status(SERVICE_NAME).current_state != 'running'
|
113
|
+
puts 'One moment...' + Service.status(SERVICE_NAME).current_state
|
114
|
+
sleep 1
|
115
|
+
end
|
116
|
+
puts 'Service ' + SERVICE_NAME + ' resumed'
|
117
|
+
else
|
118
|
+
puts 'Already running'
|
119
|
+
end
|
120
|
+
else
|
121
|
+
raise ArgumentError, 'unknown option: ' + ARGV[0]
|
122
|
+
end
|
@@ -0,0 +1,23 @@
|
|
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
|
+
require 'win32/service'
|
8
|
+
include Win32
|
9
|
+
|
10
|
+
puts "VERSION: " + Service::VERSION
|
11
|
+
|
12
|
+
p Service.exists?("ClipSrv")
|
13
|
+
p Service.exists?("foo")
|
14
|
+
|
15
|
+
status = Service.status("ClipSrv")
|
16
|
+
p status
|
17
|
+
|
18
|
+
info = Service.config_info("ClipSrv")
|
19
|
+
p info
|
20
|
+
|
21
|
+
Service.services{ |struct|
|
22
|
+
p struct
|
23
|
+
}
|
data/ext/extconf.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'mkmf'
|
2
|
-
|
3
|
-
dir_config('win32-daemon')
|
4
|
-
have_func('RegisterServiceCtrlHandlerEx')
|
5
|
-
create_makefile('win32/daemon', 'win32')
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
dir_config('win32-daemon')
|
4
|
+
have_func('RegisterServiceCtrlHandlerEx')
|
5
|
+
create_makefile('win32/daemon', 'win32')
|
data/ext/win32/daemon.c
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
#include <malloc.h>
|
6
6
|
#include <tchar.h>
|
7
7
|
|
8
|
-
#define WIN32_SERVICE_VERSION "0.
|
8
|
+
#define WIN32_SERVICE_VERSION "0.7.0"
|
9
9
|
|
10
10
|
static VALUE cDaemonError;
|
11
11
|
|
@@ -66,34 +66,23 @@ void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
|
|
66
66
|
{
|
67
67
|
// Obtain the name of the service.
|
68
68
|
LPTSTR lpszServiceName = lpszArgv[0];
|
69
|
-
|
69
|
+
|
70
70
|
// Args passed to Service.start
|
71
71
|
if(dwArgc > 1){
|
72
|
-
int i;
|
72
|
+
unsigned int i;
|
73
73
|
Argc = dwArgc - 1;
|
74
74
|
Argv = malloc(sizeof(VALUE)*Argc);
|
75
|
-
|
76
|
-
for(i=1;i<dwArgc;i++)
|
75
|
+
|
76
|
+
for(i=1; i < dwArgc; i++)
|
77
77
|
Argv[i-1] = rb_str_new2(lpszArgv[i]);
|
78
78
|
}
|
79
79
|
|
80
80
|
// Register the service ctrl handler.
|
81
81
|
ssh = RegisterServiceCtrlHandler(lpszServiceName,
|
82
82
|
(LPHANDLER_FUNCTION)Service_Ctrl);
|
83
|
-
|
84
|
-
// Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
|
85
|
-
// associated with a tty. This helps newbs avoid Errno::EBADF errors.
|
86
|
-
if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
|
87
|
-
rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));
|
88
|
-
|
89
|
-
if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
|
90
|
-
rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));
|
91
|
-
|
92
|
-
if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
|
93
|
-
rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));
|
94
83
|
|
95
84
|
// no service to stop, no service handle to notify, nothing to do but exit
|
96
|
-
if(ssh == (SERVICE_STATUS_HANDLE)0)
|
85
|
+
if(ssh == (SERVICE_STATUS_HANDLE)0)
|
97
86
|
return;
|
98
87
|
|
99
88
|
// The service has started.
|
@@ -274,7 +263,7 @@ DWORD WINAPI ThreadProc(LPVOID lpParameter){
|
|
274
263
|
SERVICE_TABLE_ENTRY ste[] =
|
275
264
|
{{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};
|
276
265
|
|
277
|
-
// No service to step, no service handle, no ruby exceptions, just
|
266
|
+
// No service to step, no service handle, no ruby exceptions, just
|
278
267
|
// terminate the thread.
|
279
268
|
if(!StartServiceCtrlDispatcher(ste))
|
280
269
|
return 1;
|
@@ -291,12 +280,12 @@ static VALUE daemon_allocate(VALUE klass){
|
|
291
280
|
static VALUE daemon_mainloop_protect(VALUE self)
|
292
281
|
{
|
293
282
|
if(rb_respond_to(self,rb_intern("service_main"))){
|
294
|
-
if(Argc == 0)
|
283
|
+
if(Argc == 0)
|
295
284
|
rb_funcall(self, rb_intern("service_main"), 0);
|
296
285
|
else
|
297
286
|
rb_funcall2(self, rb_intern("service_main"), Argc, Argv);
|
298
|
-
}
|
299
|
-
|
287
|
+
}
|
288
|
+
|
300
289
|
return self;
|
301
290
|
}
|
302
291
|
|
@@ -341,115 +330,126 @@ static VALUE daemon_mainloop_ensure(VALUE self)
|
|
341
330
|
*/
|
342
331
|
static VALUE daemon_mainloop(VALUE self)
|
343
332
|
{
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
333
|
+
DWORD ThreadId;
|
334
|
+
HANDLE events[2];
|
335
|
+
DWORD index;
|
336
|
+
VALUE result, EventHookHash;
|
337
|
+
int status = 0;
|
338
|
+
|
339
|
+
dwServiceState = 0;
|
340
|
+
|
341
|
+
// Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
|
342
|
+
// associated with a tty. This helps newbs avoid Errno::EBADF errors.
|
343
|
+
if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
|
344
|
+
rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));
|
349
345
|
|
350
|
-
|
346
|
+
if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
|
347
|
+
rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));
|
351
348
|
|
352
|
-
|
349
|
+
if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
|
350
|
+
rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));
|
351
|
+
|
352
|
+
// Use a markable instance variable to prevent the garbage collector
|
353
353
|
// from freeing the hash before Ruby_Service_Ctrl exits, or just
|
354
354
|
// at any ole time while running the service
|
355
355
|
EventHookHash = rb_hash_new();
|
356
356
|
rb_ivar_set(self, rb_intern("@event_hooks"), EventHookHash);
|
357
357
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
358
|
+
// Event hooks
|
359
|
+
if(rb_respond_to(self, rb_intern("service_stop"))){
|
360
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP),
|
361
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_stop"))));
|
362
|
+
}
|
363
363
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
364
|
+
if(rb_respond_to(self, rb_intern("service_pause"))){
|
365
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PAUSE),
|
366
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_pause"))));
|
367
|
+
}
|
368
368
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
369
|
+
if(rb_respond_to(self, rb_intern("service_resume"))){
|
370
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_CONTINUE),
|
371
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_resume"))));
|
372
|
+
}
|
373
373
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
374
|
+
if(rb_respond_to(self, rb_intern("service_interrogate"))){
|
375
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_INTERROGATE),
|
376
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_interrogate"))));
|
377
|
+
}
|
378
378
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
379
|
+
if(rb_respond_to(self, rb_intern("service_shutdown"))){
|
380
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_SHUTDOWN),
|
381
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_shutdown"))));
|
382
|
+
}
|
383
383
|
|
384
384
|
#ifdef SERVICE_CONTROL_PARAMCHANGE
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
385
|
+
if(rb_respond_to(self, rb_intern("service_paramchange"))){
|
386
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
|
387
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_paramchange"))));
|
388
|
+
}
|
389
389
|
#endif
|
390
390
|
|
391
391
|
#ifdef SERVICE_CONTROL_NETBINDADD
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
392
|
+
if(rb_respond_to(self, rb_intern("service_netbindadd"))){
|
393
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDADD),
|
394
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindadd"))));
|
395
|
+
}
|
396
396
|
#endif
|
397
397
|
|
398
398
|
#ifdef SERVICE_CONTROL_NETBINDREMOVE
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
399
|
+
if(rb_respond_to(self, rb_intern("service_netbindremove"))){
|
400
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
|
401
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindremove"))));
|
402
|
+
}
|
403
403
|
#endif
|
404
404
|
|
405
405
|
#ifdef SERVICE_CONTROL_NETBINDENABLE
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
406
|
+
if(rb_respond_to(self, rb_intern("service_netbindenable"))){
|
407
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
|
408
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindenable"))));
|
409
|
+
}
|
410
410
|
#endif
|
411
411
|
|
412
412
|
#ifdef SERVICE_CONTROL_NETBINDDISABLE
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
413
|
+
if(rb_respond_to(self, rb_intern("service_netbinddisable"))){
|
414
|
+
rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
|
415
|
+
rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbinddisable"))));
|
416
|
+
}
|
417
417
|
#endif
|
418
418
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
419
|
+
// Calling init here so that init failures never even tries to
|
420
|
+
// start the service... of course that means that init methods
|
421
|
+
// must be very quick, because the SCM will be receiving no
|
422
|
+
// START_PENDING messages while init's running - I may fix this
|
423
|
+
// later
|
424
|
+
if(rb_respond_to(self, rb_intern("service_init")))
|
425
|
+
rb_funcall(self, rb_intern("service_init"),0);
|
426
426
|
|
427
|
-
|
428
|
-
|
427
|
+
// Create the event to signal the service to start.
|
428
|
+
hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
429
429
|
|
430
|
-
|
431
|
-
|
430
|
+
if(hStartEvent == NULL)
|
431
|
+
rb_raise(cDaemonError, ErrorDescription(GetLastError()));
|
432
432
|
|
433
|
-
|
434
|
-
|
433
|
+
// Create the event to signal the service to stop.
|
434
|
+
hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
435
435
|
|
436
|
-
|
437
|
-
|
436
|
+
if(hStopEvent == NULL)
|
437
|
+
rb_raise(cDaemonError, ErrorDescription(GetLastError()));
|
438
438
|
|
439
|
-
|
440
|
-
|
439
|
+
// Create the event to signal the service that stop has completed
|
440
|
+
hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
441
441
|
|
442
|
-
|
443
|
-
|
442
|
+
if(hStopCompletedEvent == NULL)
|
443
|
+
rb_raise(cDaemonError, ErrorDescription(GetLastError()));
|
444
444
|
|
445
|
-
|
446
|
-
|
445
|
+
// Create Thread for service main
|
446
|
+
hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
|
447
447
|
|
448
|
-
|
449
|
-
|
448
|
+
if(hThread == INVALID_HANDLE_VALUE)
|
449
|
+
rb_raise(cDaemonError, ErrorDescription(GetLastError()));
|
450
450
|
|
451
|
-
|
452
|
-
|
451
|
+
events[0] = hThread;
|
452
|
+
events[1] = hStartEvent;
|
453
453
|
|
454
454
|
// wait for Service_Main function to either start the service OR terminate
|
455
455
|
while((index = WaitForMultipleObjects(2,events,FALSE,1000)) == WAIT_TIMEOUT)
|
@@ -480,10 +480,10 @@ static VALUE daemon_mainloop(VALUE self)
|
|
480
480
|
/*
|
481
481
|
* Returns the state of the service (as an constant integer) which can be any
|
482
482
|
* of the service status constants, e.g. RUNNING, PAUSED, etc.
|
483
|
-
*
|
483
|
+
*
|
484
484
|
* This method is typically used within your service_main method to setup the
|
485
485
|
* loop. For example:
|
486
|
-
*
|
486
|
+
*
|
487
487
|
* class MyDaemon < Daemon
|
488
488
|
* def service_main
|
489
489
|
* while state == RUNNING || state == PAUSED || state == IDLE
|
@@ -491,7 +491,7 @@ static VALUE daemon_mainloop(VALUE self)
|
|
491
491
|
* end
|
492
492
|
* end
|
493
493
|
* end
|
494
|
-
*
|
494
|
+
*
|
495
495
|
* See the Daemon#running? method for an abstraction of the above code.
|
496
496
|
*/
|
497
497
|
static VALUE daemon_state(VALUE self){
|
@@ -501,10 +501,10 @@ static VALUE daemon_state(VALUE self){
|
|
501
501
|
/*
|
502
502
|
* Returns whether or not the service is in a running state, i.e. the service
|
503
503
|
* status is either RUNNING, PAUSED or IDLE.
|
504
|
-
*
|
504
|
+
*
|
505
505
|
* This is typically used within your service_main method to setup the main
|
506
506
|
* loop. For example:
|
507
|
-
*
|
507
|
+
*
|
508
508
|
* class MyDaemon < Daemon
|
509
509
|
* def service_main
|
510
510
|
* while running?
|
@@ -522,11 +522,11 @@ static VALUE daemon_is_running(VALUE self){
|
|
522
522
|
){
|
523
523
|
v_bool = Qtrue;
|
524
524
|
}
|
525
|
-
|
526
|
-
return v_bool;
|
525
|
+
|
526
|
+
return v_bool;
|
527
527
|
}
|
528
528
|
|
529
|
-
/*
|
529
|
+
/*
|
530
530
|
* This is a shortcut for Daemon.new + Daemon#mainloop.
|
531
531
|
*/
|
532
532
|
static VALUE daemon_c_mainloop(VALUE klass){
|
@@ -552,17 +552,17 @@ void Init_daemon()
|
|
552
552
|
rb_define_method(cDaemon, "mainloop", daemon_mainloop, 0);
|
553
553
|
rb_define_method(cDaemon, "state", daemon_state, 0);
|
554
554
|
rb_define_method(cDaemon, "running?", daemon_is_running, 0);
|
555
|
-
|
555
|
+
|
556
556
|
rb_define_singleton_method(cDaemon, "mainloop", daemon_c_mainloop, 0);
|
557
557
|
|
558
558
|
// Intialize critical section used by green polling thread
|
559
559
|
InitializeCriticalSection(&csControlCode);
|
560
560
|
|
561
561
|
// Constants
|
562
|
-
|
563
|
-
/* 0.
|
562
|
+
|
563
|
+
/* 0.7.0: The version of this library */
|
564
564
|
rb_define_const(cDaemon, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
|
565
|
-
|
565
|
+
|
566
566
|
/* Service has received a signal to resume but is not yet running */
|
567
567
|
rb_define_const(cDaemon, "CONTINUE_PENDING",
|
568
568
|
INT2NUM(SERVICE_CONTINUE_PENDING));
|
@@ -590,7 +590,7 @@ void Init_daemon()
|
|
590
590
|
/* Service is stopped. */
|
591
591
|
rb_define_const(cDaemon, "STOPPED",
|
592
592
|
INT2NUM(SERVICE_STOPPED));
|
593
|
-
|
593
|
+
|
594
594
|
/* Service is in an idle state */
|
595
595
|
rb_define_const(cDaemon, "IDLE", INT2NUM(0));
|
596
596
|
}
|