win32-service 0.6.1-x86-mswin32-60 → 0.7.0-x86-mswin32-60
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.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
|