win32-service 0.6.1-x86-mswin32-60 → 0.7.0-x86-mswin32-60
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.rb +89 -0
- data/examples/demo_daemon_ctl.rb +122 -0
- data/examples/demo_services.rb +23 -0
- data/ext/win32/daemon.c +596 -0
- 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 -19
- 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,89 @@
|
|
1
|
+
LOG_FILE = 'C:\\test.log'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'win32/daemon'
|
5
|
+
include Win32
|
6
|
+
|
7
|
+
class DemoDaemon < Daemon
|
8
|
+
# This method fires off before the +service_main+ mainloop is entered.
|
9
|
+
# Any pre-setup code you need to run before your service's mainloop
|
10
|
+
# starts should be put here. Otherwise the service might fail with a
|
11
|
+
# timeout error when you try to start it.
|
12
|
+
#
|
13
|
+
def service_init
|
14
|
+
10.times{ |i|
|
15
|
+
File.open(LOG_FILE , 'a'){ |f| f.puts("#{i}") }
|
16
|
+
sleep 1
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
# This is the daemon's mainloop. In other words, whatever runs here
|
21
|
+
# is the code that runs while your service is running. Note that the
|
22
|
+
# loop is not implicit.
|
23
|
+
#
|
24
|
+
# You must setup a loop as I've done here with the 'while running?'
|
25
|
+
# code, or setup your own loop. Otherwise your service will exit and
|
26
|
+
# won't be especially useful.
|
27
|
+
#
|
28
|
+
# In this particular case, I've setup a loop to append a short message
|
29
|
+
# and timestamp to a file on your C: drive every 20 seconds. Be sure
|
30
|
+
# to stop the service when you're done!
|
31
|
+
#
|
32
|
+
def service_main(*args)
|
33
|
+
msg = 'service_main entered at: ' + Time.now.to_s
|
34
|
+
File.open(LOG_FILE, 'a'){ |f|
|
35
|
+
f.puts msg
|
36
|
+
f.puts "Args: " + args.join(',')
|
37
|
+
}
|
38
|
+
|
39
|
+
while running?
|
40
|
+
if state == RUNNING
|
41
|
+
sleep 20
|
42
|
+
msg = 'Service is running as of: ' + Time.now.to_s
|
43
|
+
File.open(LOG_FILE, 'a'){ |f| f.puts msg }
|
44
|
+
else
|
45
|
+
sleep 0.5
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
File.open(LOG_FILE, 'a'){ |f| f.puts "STATE: #{state}" }
|
50
|
+
|
51
|
+
msg = 'service_main left at: ' + Time.now.to_s
|
52
|
+
File.open(LOG_FILE, 'a'){ |f| f.puts msg }
|
53
|
+
end
|
54
|
+
|
55
|
+
# This event triggers when the service receives a signal to stop. I've
|
56
|
+
# added an explicit "exit!" here to ensure that the Ruby interpreter exits
|
57
|
+
# properly. I use 'exit!' instead of 'exit' because otherwise Ruby will
|
58
|
+
# raise a SystemExitError, which I don't want.
|
59
|
+
#
|
60
|
+
def service_stop
|
61
|
+
msg = 'Received stop signal at: ' + Time.now.to_s
|
62
|
+
File.open(LOG_FILE, 'a'){ |f| f.puts msg }
|
63
|
+
exit!
|
64
|
+
end
|
65
|
+
|
66
|
+
# This event triggers when the service receives a signal to pause.
|
67
|
+
#
|
68
|
+
def service_pause
|
69
|
+
msg = 'Received pause signal at: ' + Time.now.to_s
|
70
|
+
File.open(LOG_FILE, 'a'){ |f| f.puts msg }
|
71
|
+
end
|
72
|
+
|
73
|
+
# This event triggers when the service receives a signal to resume
|
74
|
+
# from a paused state.
|
75
|
+
#
|
76
|
+
def service_resume
|
77
|
+
msg = 'Received resume signal at: ' + Time.now.to_s
|
78
|
+
File.open(LOG_FILE, 'a'){ |f| f.puts msg }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Create an instance of the Daemon and put it into a loop. I borrowed the
|
83
|
+
# method name 'mainloop' from Tk, btw.
|
84
|
+
#
|
85
|
+
DemoDaemon.mainloop
|
86
|
+
rescue Exception => err
|
87
|
+
File.open(LOG_FILE, 'a'){ |fh| fh.puts 'Daemon failure: ' + err }
|
88
|
+
raise
|
89
|
+
end
|
@@ -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
|