win32-service 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|