svdir 0.2
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/CHANGELOG +29 -0
- data/LICENSE +19 -0
- data/README +67 -0
- data/Rakefile +66 -0
- data/example/svctl +117 -0
- data/lib/sys/sv/statusbytes.rb +53 -0
- data/lib/sys/sv/svdir.rb +224 -0
- data/lib/sys/sv/util.rb +36 -0
- data/test/fixtures/PrefabSvDir.rb +22 -0
- data/test/fixtures/TempSvDir.rb +62 -0
- data/test/services/corrupt-normd/down +0 -0
- data/test/services/corrupt-normd/supervise/ok +0 -0
- data/test/services/corrupt-normd/supervise/status +0 -0
- data/test/services/corrupt-normu/supervise/ok +0 -0
- data/test/services/corrupt-normu/supervise/status +0 -0
- data/test/services/corrupt-zero-normd/down +0 -0
- data/test/services/corrupt-zero-normd/supervise/ok +0 -0
- data/test/services/corrupt-zero-normd/supervise/status +0 -0
- data/test/services/corrupt-zero-normu/supervise/ok +0 -0
- data/test/services/corrupt-zero-normu/supervise/status +0 -0
- data/test/services/down-0-normd-nowant/down +0 -0
- data/test/services/down-0-normd-nowant/supervise/ok +0 -0
- data/test/services/down-0-normd-nowant/supervise/status +0 -0
- data/test/services/down-0-normu-nowant/supervise/ok +0 -0
- data/test/services/down-0-normu-nowant/supervise/status +0 -0
- data/test/services/up-12526-normu-wantd/supervise/ok +0 -0
- data/test/services/up-12526-normu-wantd/supervise/status +0 -0
- data/test/services/up-12581-normd-wantd-paused/down +0 -0
- data/test/services/up-12581-normd-wantd-paused/supervise/ok +0 -0
- data/test/services/up-12581-normd-wantd-paused/supervise/status +0 -0
- data/test/services/up-12581-normu-wantd-paused/supervise/ok +0 -0
- data/test/services/up-12581-normu-wantd-paused/supervise/status +0 -0
- data/test/services/up-12816-normd-nowant/down +0 -0
- data/test/services/up-12816-normd-nowant/supervise/ok +0 -0
- data/test/services/up-12816-normd-nowant/supervise/status +0 -0
- data/test/services/up-12816-normu-nowant/supervise/ok +0 -0
- data/test/services/up-12816-normu-nowant/supervise/status +0 -0
- data/test/services/up-12868-normd-wantu-paused/down +0 -0
- data/test/services/up-12868-normd-wantu-paused/supervise/ok +0 -0
- data/test/services/up-12868-normd-wantu-paused/supervise/status +0 -0
- data/test/services/up-12868-normd-wantu/down +0 -0
- data/test/services/up-12868-normd-wantu/supervise/ok +0 -0
- data/test/services/up-12868-normd-wantu/supervise/status +0 -0
- data/test/services/up-12868-normu-wantu-paused/supervise/ok +0 -0
- data/test/services/up-12868-normu-wantu-paused/supervise/status +0 -0
- data/test/services/up-12868-normu-wantu/supervise/ok +0 -0
- data/test/services/up-12868-normu-wantu/supervise/status +0 -0
- data/test/services/up-16464-normd-wantd/down +0 -0
- data/test/services/up-16464-normd-wantd/supervise/ok +0 -0
- data/test/services/up-16464-normd-wantd/supervise/status +0 -0
- data/test/testbase.rb +30 -0
- data/test/ts_corrupt.rb +133 -0
- data/test/ts_nosupervisor.rb +81 -0
- data/test/ts_signal.rb +128 -0
- data/test/ts_svstat.rb +548 -0
- metadata +117 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
2011-02-28 mjp version 0.2
|
2
|
+
[doc] Documentation touch-up, move from Qualys SVN to github.
|
3
|
+
|
4
|
+
2011-02-17 mjp
|
5
|
+
[api] Remove #epoch() method, #pid() now returns nil if service is not
|
6
|
+
running (formerly returned zero).
|
7
|
+
[int] Remove Forwardable delegation, refactor SvDir and StatusBytes
|
8
|
+
accordingly. A skosh more efficient, but also puts method documentation
|
9
|
+
in the class that needs it.
|
10
|
+
[tst] Update tests for API changes, delegation refactoring and corrupt
|
11
|
+
status file handling.
|
12
|
+
[doc] update LICENSE, copyright notice, README
|
13
|
+
[pkg] remove Echoe dependency, generate gem with vanilla rake tasks
|
14
|
+
(Rakefile)
|
15
|
+
|
16
|
+
2008-10-13 mjp
|
17
|
+
[api] make that ENXIO/EWHATEVER on non-running supervisor. Raise
|
18
|
+
EPROTO specifically on truncated 'status' file.
|
19
|
+
[tst] Fixtures::TempSvDir makes bona fide FIFOs, correctly makes
|
20
|
+
incremented tempdirs. ts_control renamed ts_signal, also test non-
|
21
|
+
running supervisor (ts_nosupervisor).
|
22
|
+
[doc] note Errno:: conventions, use "supervisor" consistently.
|
23
|
+
|
24
|
+
2008-10-10 mjp
|
25
|
+
[bug] qualify 'ok' FIFO path (cpforbes).
|
26
|
+
[api] raise EPROTO for non-running supervisor.
|
27
|
+
|
28
|
+
2008-10-06 mjp
|
29
|
+
Initial check-in.
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Qualys, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
Copyright (c) 2010, 2011 Qualys, Inc.
|
2
|
+
|
3
|
+
Copyright (c) 2008 - 2010 Nemean Networks, LLC.
|
4
|
+
|
5
|
+
= Overview
|
6
|
+
|
7
|
+
+svdir+ is a Ruby interface to the "service directory" style of robust daemon
|
8
|
+
process supervision introducted in Dan Bernstein's +daemontools+ software and
|
9
|
+
compatibly extended in Gerit Pape's +runit+ package.
|
10
|
+
|
11
|
+
It exposes a programmatic interface to reliably starting, stopping, signalling
|
12
|
+
and interrogating services all implemented directly -- no need to shell out to
|
13
|
+
separate utilities. See the documentation for Sys::Sv::SvDir for complete
|
14
|
+
information.
|
15
|
+
|
16
|
+
More information on +daemontools+ is available at
|
17
|
+
http://cr.yp.to/daemontools.html. More information on +runit+ is available at
|
18
|
+
http://smarden.org/runit/.
|
19
|
+
|
20
|
+
== Example
|
21
|
+
|
22
|
+
The <tt>example/</tt> subdirectory in the source distribution contains a
|
23
|
+
demonstration program which a system adminstrator might use to control daemons.
|
24
|
+
|
25
|
+
Typical programmatic use of this software might look like this:
|
26
|
+
|
27
|
+
#! /usr/bin/env ruby
|
28
|
+
|
29
|
+
require 'sys/sv/svdir'
|
30
|
+
include Sys::Sv
|
31
|
+
|
32
|
+
# shut down all daemons running under /service
|
33
|
+
Dir["/service"].each do |svpath|
|
34
|
+
s = SvDir.new(svpath)
|
35
|
+
|
36
|
+
# force the .../log to shut down, ignoring services without loggers
|
37
|
+
s.log.signal(:exit) rescue nil
|
38
|
+
|
39
|
+
pid = s.pid
|
40
|
+
if pid != 0
|
41
|
+
s.signal(:exit)
|
42
|
+
puts "Told #{s.path} (pid #{pid}) to exit"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
= Installation
|
47
|
+
|
48
|
+
Look for the gem on http://rubygems.org.
|
49
|
+
|
50
|
+
`rake package` will build a .gem under pkg/, and `rake rdoc` will
|
51
|
+
generate module documentation.
|
52
|
+
|
53
|
+
`rake test` and `rake rcov` will give a good idea of where the code is.
|
54
|
+
|
55
|
+
= To Do
|
56
|
+
|
57
|
+
* Further testing
|
58
|
+
- log()
|
59
|
+
- TAI64 testing
|
60
|
+
|
61
|
+
* Possible extensions
|
62
|
+
- <tt>SvDir.new(d) &block</tt> - persist StatusBytes object for block?
|
63
|
+
- <tt>normally_down!</tt> and <tt>normally_up!</tt>
|
64
|
+
|
65
|
+
= Author
|
66
|
+
|
67
|
+
Mike Pomraning ("mpomraning" at "qualys" dot "com")
|
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
FILES = FileList['Rakefile', 'CHANGELOG', 'LICENSE', 'README',
|
7
|
+
'lib/**/*.rb', 'test/**/*', 'example/*'].to_a
|
8
|
+
TESTS = FileList['test/ts_*.rb']
|
9
|
+
|
10
|
+
spec = Gem::Specification.new do |s|
|
11
|
+
s.platform = Gem::Platform::RUBY
|
12
|
+
s.author = "Mike Pomraning"
|
13
|
+
s.summary = "An interface to service directories ala supervise/runsv"
|
14
|
+
s.name = 'svdir'
|
15
|
+
s.version = '0.2'
|
16
|
+
s.require_path = 'lib'
|
17
|
+
s.description = <<eodesc
|
18
|
+
The svdir package controls service directories, a scheme for reliably
|
19
|
+
controlling daemon processes as implemented in Dan Bernstein's daemontools
|
20
|
+
software ("supervise") or Gerit Pape's runit software ("runsv").
|
21
|
+
eodesc
|
22
|
+
s.files = FILES
|
23
|
+
s.test_files = TESTS
|
24
|
+
s.has_rdoc = true
|
25
|
+
end
|
26
|
+
|
27
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
28
|
+
pkg.package_dir = 'pkg'
|
29
|
+
pkg.need_tar = true
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Generate rdoc"
|
33
|
+
Rake::RDocTask.new("rdoc") do |rdoc|
|
34
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
35
|
+
rdoc.title = "svdir"
|
36
|
+
# Show source inline with line numbers
|
37
|
+
rdoc.options << "--inline-source" << "--line-numbers"
|
38
|
+
# Make the readme file the start page for the generated html
|
39
|
+
rdoc.options << '--main' << 'README'
|
40
|
+
rdoc.rdoc_files.include('lib/**/*.rb',
|
41
|
+
'CHANGELOG',
|
42
|
+
'README',
|
43
|
+
'LICENSE')
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Run included tests"
|
47
|
+
Rake::TestTask.new do |t|
|
48
|
+
t.libs << "test"
|
49
|
+
t.test_files = TESTS
|
50
|
+
t.libs << "lib"
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Run test coverage (rcov)"
|
54
|
+
begin
|
55
|
+
require 'rcov/rcovtask'
|
56
|
+
Rcov::RcovTask.new do |t|
|
57
|
+
t.test_files = TESTS
|
58
|
+
t.libs << "test" << "lib"
|
59
|
+
t.rcov_opts << '--exclude /gems/,/Library/,/usr/,spec,lib/tasks'
|
60
|
+
end
|
61
|
+
rescue LoadError
|
62
|
+
task :rcov do
|
63
|
+
puts "Error - you seem to be missing 'rcov'"
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
end
|
data/example/svctl
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sys/sv/svdir'
|
4
|
+
include Sys::Sv
|
5
|
+
|
6
|
+
USAGE = <<'__eousage'
|
7
|
+
Usage: svctl [cmd] service_dir [service_dir ...]
|
8
|
+
svctl [-h|--help]
|
9
|
+
|
10
|
+
If 'cmd' is 'status', print a summary of the current state of each
|
11
|
+
given service directory and its subordinate log directory, if any.
|
12
|
+
|
13
|
+
Issue 'cmd' to the given service directories in turn. Behavior
|
14
|
+
varies with the command:
|
15
|
+
|
16
|
+
status ...... summarize status of service and log service, if any
|
17
|
+
up .......... start and, as needed, restart the service
|
18
|
+
down ........ TERM+CONT a running service, do not restart
|
19
|
+
exit ........ "down" a service, TERM its log service, then exit
|
20
|
+
once ........ start but do not restart the service
|
21
|
+
|
22
|
+
Other values of 'cmd' instruct the supervisory process to send a
|
23
|
+
UNIX signal to its service, if running:
|
24
|
+
|
25
|
+
pause, STOP, continue, CONT, hangup, HUP, alarm, ALRM, interrupt, INT,
|
26
|
+
terminate, TERM, kill, KILL, user1, USR1, user2, USR2
|
27
|
+
|
28
|
+
Note that the 'user'/'USR' commands are extensions to the original
|
29
|
+
process supervision implementation, and not supported by all supervisors.
|
30
|
+
__eousage
|
31
|
+
|
32
|
+
def usage_exit(errmsg = nil)
|
33
|
+
outio, rc = [$stdout, 0]
|
34
|
+
|
35
|
+
if errmsg
|
36
|
+
outio = $stderr
|
37
|
+
rc = 1
|
38
|
+
outio.puts errmsg
|
39
|
+
end
|
40
|
+
|
41
|
+
outio.puts USAGE
|
42
|
+
|
43
|
+
exit(rc)
|
44
|
+
end
|
45
|
+
|
46
|
+
def formatted_status(sv, name = nil)
|
47
|
+
# sv(1)-inspired status
|
48
|
+
#
|
49
|
+
# For each SvDir argument, show:
|
50
|
+
# current state: running, paused ("running" but SIGSTOP'd), or down
|
51
|
+
# service name: supplied by caller
|
52
|
+
# elapsed time: time since begun running or since stopped
|
53
|
+
# pid: if applicable
|
54
|
+
# typical state: normally up/down? (only if different than current)
|
55
|
+
# want up/down?: if applicable
|
56
|
+
#
|
57
|
+
name = File.basename(sv.path) unless name
|
58
|
+
|
59
|
+
runstate, elapsed, pid_str, unusual_state = [nil, nil, nil, nil]
|
60
|
+
|
61
|
+
if sv.up?
|
62
|
+
runstate = sv.paused? ? "paused" : "run"
|
63
|
+
elapsed = sv.uptime
|
64
|
+
pid_str = "(pid #{sv.pid})"
|
65
|
+
unusual_state = "normally down" if sv.normally_down? # Hmm!
|
66
|
+
else
|
67
|
+
runstate = "down"
|
68
|
+
elapsed = sv.downtime
|
69
|
+
unusual_state = "normally up" if sv.normally_up? # Hmm!
|
70
|
+
end
|
71
|
+
|
72
|
+
elapsed = elapsed.round.to_s + 's'
|
73
|
+
want_state = sv.want_up? ? "want up" :
|
74
|
+
sv.want_down? ? "want down" :
|
75
|
+
""
|
76
|
+
|
77
|
+
["#{runstate}: #{name}:", pid_str, elapsed, unusual_state].compact.join(" ")
|
78
|
+
rescue SystemCallError => e
|
79
|
+
"err: #{name}: No supervisor detected (#{e})"
|
80
|
+
end
|
81
|
+
|
82
|
+
def do_signal(sig, *dirs)
|
83
|
+
dirs.each do |dir|
|
84
|
+
begin
|
85
|
+
SvDir.new(dir).signal(sig)
|
86
|
+
rescue SystemCallError => e
|
87
|
+
$stderr.puts "error signalling #{dir}: #{e.message}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def main
|
93
|
+
# A quick demonstration of Sys::Sv::SvDir ...
|
94
|
+
cmd = ARGV.shift
|
95
|
+
|
96
|
+
case cmd
|
97
|
+
when '--help', '-h', nil
|
98
|
+
usage_exit
|
99
|
+
when 'status'
|
100
|
+
ARGV.each do |dir|
|
101
|
+
sv = SvDir.new(dir)
|
102
|
+
sv_stat = formatted_status(sv, dir)
|
103
|
+
if log = sv.log
|
104
|
+
sv_stat += '; ' + formatted_status(log, 'log')
|
105
|
+
end
|
106
|
+
puts sv_stat
|
107
|
+
end
|
108
|
+
else
|
109
|
+
begin
|
110
|
+
do_signal(cmd, *ARGV)
|
111
|
+
rescue Exception => e
|
112
|
+
usage_exit("error: #{e.message}")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
main if __FILE__ == $0
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Mike Pomraning
|
3
|
+
# Copyright:: Copyright (c) 2011 Qualys, Inc.
|
4
|
+
# License:: MIT (see the file LICENSE)
|
5
|
+
#
|
6
|
+
|
7
|
+
module Sys # :nodoc:
|
8
|
+
module Sv # :nodoc:
|
9
|
+
|
10
|
+
# The StatusBytes class interprets state files maintained by a SvDir's
|
11
|
+
# _monitor_ process. It should normally not be instantiated directly.
|
12
|
+
class StatusBytes # :nodoc:
|
13
|
+
BUFLEN = 18 # TODO - grok runit extended info (20 bytes)
|
14
|
+
TAI_EPOCH = 4611686018427387914 # time_t 0 on the TAI scale
|
15
|
+
|
16
|
+
attr_reader :pid, :pauseflag, :wantflag
|
17
|
+
|
18
|
+
def initialize(bytes) # :nodoc:
|
19
|
+
if bytes.size < BUFLEN
|
20
|
+
raise ::Errno::EPROTO.new("corrupt status buffer")
|
21
|
+
end
|
22
|
+
|
23
|
+
@bytes = bytes
|
24
|
+
@pid, @pauseflag, @wantflag = @bytes.unpack('x12 V c a')
|
25
|
+
@epoch = nil # computed if needed
|
26
|
+
end
|
27
|
+
|
28
|
+
# Number of seconds since service was most recently started or
|
29
|
+
# stopped.
|
30
|
+
def elapsed
|
31
|
+
::Time.now.to_f - epoch()
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the number of seconds since the UNIX epoch since the
|
35
|
+
# service was most recently started or stopped.
|
36
|
+
def epoch
|
37
|
+
return @epoch if @epoch
|
38
|
+
|
39
|
+
# assemble UNIX-scale seconds from TAI64N label
|
40
|
+
hi32, lo32, nano = @bytes.unpack('N N N')
|
41
|
+
@epoch = (hi32 << 32) + lo32 - TAI_EPOCH
|
42
|
+
if @epoch <= 0
|
43
|
+
@epoch = 0.0
|
44
|
+
else
|
45
|
+
@epoch += nano/10e9
|
46
|
+
end
|
47
|
+
|
48
|
+
@epoch
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end #-- module Sv
|
53
|
+
end #-- module Sys
|
data/lib/sys/sv/svdir.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Author:: Mike Pomraning
|
4
|
+
# Copyright:: Copyright (c) 2011 Qualys, Inc.
|
5
|
+
# License:: MIT (see the file LICENSE)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'sys/sv/statusbytes' # class StatusBytes
|
9
|
+
require 'sys/sv/util' # Util::open_write(), open_read()
|
10
|
+
|
11
|
+
module Sys # :nodoc
|
12
|
+
module Sv # :nodoc:
|
13
|
+
|
14
|
+
# The SvDir class encapsulates service directories, a scheme for
|
15
|
+
# reliably controlling daemon processes (services) introduced in Dan
|
16
|
+
# Bernstein's +daemontools+ software.
|
17
|
+
#
|
18
|
+
# Each service is monitored by a _supervisor_, which is responsible
|
19
|
+
# for starting, stopping, restarting and generally controlling the
|
20
|
+
# service. Examples of such supervisors include +supervise+ from
|
21
|
+
# the +daemontools+ package and +runsv+ from Gerit Pape's compatible
|
22
|
+
# +runit+ software.
|
23
|
+
#
|
24
|
+
# Most SvDir methods will raise <tt>Errno::</tt> exceptions (each
|
25
|
+
# a subclass of +SystemCallException+) if the underlying filesystem
|
26
|
+
# representation of the service directory is not as expected. For
|
27
|
+
# example, +EACCESS+ or +ENOENT+ may be raised if the _supervisor_'s
|
28
|
+
# status directory or state files are missing or unreadable. Additionally,
|
29
|
+
# +ENXIO+ is raised if the _supervisor_ itself isn't running.
|
30
|
+
#
|
31
|
+
|
32
|
+
class SvDir
|
33
|
+
|
34
|
+
VERSION = '0.2'
|
35
|
+
|
36
|
+
attr_reader :path
|
37
|
+
|
38
|
+
# ...SvDir::Commands = { :alarm => 'a', :ALRM => 'a', :exit => 'x' ... }
|
39
|
+
begin
|
40
|
+
h = {}
|
41
|
+
{
|
42
|
+
[:up ] => 'u',
|
43
|
+
[:down ] => 'd',
|
44
|
+
[:once ] => 'o',
|
45
|
+
[:pause, :STOP ] => 'p',
|
46
|
+
[:continue, :CONT ] => 'c',
|
47
|
+
[:hangup, :HUP ] => 'h',
|
48
|
+
[:alarm, :ALRM ] => 'a',
|
49
|
+
[:interrupt, :INT ] => 'i',
|
50
|
+
[:terminate, :TERM ] => 't',
|
51
|
+
[:kill, :KILL ] => 'k',
|
52
|
+
[:exit ] => 'x',
|
53
|
+
[:user1, :USR1 ] => '1', # runit and some patches to daemontools
|
54
|
+
[:user2, :USR2 ] => '2',
|
55
|
+
}.each do | cmds, byte |
|
56
|
+
cmds.each { |c| h[c] = byte }
|
57
|
+
end
|
58
|
+
const_set(:Commands, h)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create a new SvDir corresponding to the service directory +path+.
|
62
|
+
def initialize(path)
|
63
|
+
@path = path
|
64
|
+
end
|
65
|
+
|
66
|
+
# Send a signal to the service via its _supervisor_.
|
67
|
+
#
|
68
|
+
# [<tt>:up</tt>] start the service if not running, restarting as necessary.
|
69
|
+
# [<tt>:down</tt>] stop the service, issuing a TERM followed by a CONT. Do not restart it if it stops.
|
70
|
+
# [<tt>:once</tt>] start the service if not running, but do not restart it if it stops.
|
71
|
+
# [<tt>:pause</tt> or <tt>:STOP</tt>] issue a STOP signal. See also #paused?.
|
72
|
+
# [<tt>:continue</tt> or <tt>:CONT</tt>] issue a CONT signal. See also #paused?.
|
73
|
+
# [<tt>:hangup</tt> or <tt>:HUP</tt>] issue a HUP signal.
|
74
|
+
# [<tt>:alarm</tt> or <tt>:ALRM</tt>] issue an ALRM signal.
|
75
|
+
# [<tt>:interrupt</tt> or <tt>INT</tt>] issue an INT signal.
|
76
|
+
# [<tt>:terminate</tt> or <tt>TERM</tt>] issue a TERM signal.
|
77
|
+
# [<tt>:kill</tt> or <tt>KILL</tt>] issue a KILL signal.
|
78
|
+
# [<tt>:exit</tt>] tell the _supervisor_ to exit as soon as the service stops.
|
79
|
+
# [<tt>:user1</tt> or <tt>:USR1</tt>] issue a USR1 signal. <i>Not supported by all supervisors</i>
|
80
|
+
# [<tt>:user2</tt> or <tt>:USR2</tt>] issue a USR2 signal. <i>Not supported by all supervisors</i>
|
81
|
+
def signal(cmd)
|
82
|
+
unless byte = Commands[cmd.to_sym]
|
83
|
+
raise ArgumentError.new("unsupported SvDir signal `#{cmd}'")
|
84
|
+
end
|
85
|
+
Util::open_write(svfn('control')) do |f|
|
86
|
+
f.syswrite(byte)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return a SvDir object representing this service's attendant +log+
|
91
|
+
# service, otherwise +nil+.
|
92
|
+
#
|
93
|
+
# Typical SvDir daemons contain a "nested" SvDir responsible for
|
94
|
+
# logging the stdout of the base service. E.g.:
|
95
|
+
#
|
96
|
+
# /path/to/services/webserver # <--- base service
|
97
|
+
# /path/to/services/webserver/log # <--- logger
|
98
|
+
#
|
99
|
+
# The +log+ service is supervised and controllable just like the base
|
100
|
+
# service. (Also, the pipe connecting the base service's stdout to
|
101
|
+
# the +log+ service's stdin is maintained by a common parent, so that
|
102
|
+
# restarting either service won't lose data in the pipe.)
|
103
|
+
#
|
104
|
+
# Example:
|
105
|
+
#
|
106
|
+
# my_serv = Sys::Sv::SvDir.new("path/to/my_serv")
|
107
|
+
# my_serv.signal(:down)
|
108
|
+
# if logger = my_serv.log
|
109
|
+
# logger.signal(:down)
|
110
|
+
# end
|
111
|
+
def log
|
112
|
+
fn = File.join(@path, 'log')
|
113
|
+
return self.class.new(fn) if File.exists? fn
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns +true+ if the service directory's _supervisor_ is running.
|
117
|
+
#
|
118
|
+
# To determine whether the service itself is running, see #up?, #down?
|
119
|
+
# and #paused?
|
120
|
+
def svok?
|
121
|
+
Util::open_write(svfn('ok')) { true }
|
122
|
+
rescue Errno::ENXIO, Errno::ENOENT
|
123
|
+
false # No pipe reader, or no pipe!
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns +true+ if the service is typically running, i.e., if the
|
127
|
+
# service directory lacks a <tt>./down</tt> file.
|
128
|
+
#
|
129
|
+
# Note that this method functions whether or not the service is
|
130
|
+
# running, and whether or not a supervisor is running.
|
131
|
+
#
|
132
|
+
# See also the #want_up? method documentation.
|
133
|
+
def normally_up?
|
134
|
+
! normally_down?
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns +true+ if a _supervisor_ will not start the service without
|
138
|
+
# explicit instruction to do so.
|
139
|
+
#
|
140
|
+
# Note that this method functions whether or not the service is
|
141
|
+
# running, and whether or not a supervisor is running.
|
142
|
+
#
|
143
|
+
# See also the #want_down? method documentation.
|
144
|
+
def normally_down?
|
145
|
+
File.exists? File.join(@path, 'down')
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns the number of seconds the service has been down,
|
149
|
+
# as a float, or +nil+ if the service is in fact running.
|
150
|
+
def downtime
|
151
|
+
return elapsed() if down?
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the number of seconds the service has been running,
|
155
|
+
# as a float, or +nil+ if the service is not running.
|
156
|
+
def uptime
|
157
|
+
return elapsed() if up?
|
158
|
+
end
|
159
|
+
|
160
|
+
# Return the pid of the service running under this SvDir, or
|
161
|
+
# +nil+ if no service is running.
|
162
|
+
def pid
|
163
|
+
p = statusbytes.pid
|
164
|
+
return p == 0 ? nil : p
|
165
|
+
end
|
166
|
+
|
167
|
+
# +true+ if the service is not running.
|
168
|
+
def down?
|
169
|
+
pid.nil?
|
170
|
+
end
|
171
|
+
|
172
|
+
# +true+ if the service is running, even if paused.
|
173
|
+
def up?
|
174
|
+
!down?
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns +true+ if the service is paused, that is, has received
|
178
|
+
# a SIGSTOP.
|
179
|
+
def paused?
|
180
|
+
statusbytes.pauseflag != 0
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns +true+ if the service's supervisor has been instructed to
|
184
|
+
# bring the service down.
|
185
|
+
def want_down?
|
186
|
+
statusbytes.wantflag == 'd'
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns +true+ if the service's supervisor has been instructed to
|
190
|
+
# bring the service up.
|
191
|
+
def want_up?
|
192
|
+
statusbytes.wantflag == 'u'
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
def elapsed
|
198
|
+
statusbytes.elapsed
|
199
|
+
end
|
200
|
+
|
201
|
+
def svfn(basename)
|
202
|
+
File.join(@path, 'supervise', basename)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Instantiate our one-time, delegate helper class
|
206
|
+
def statusbytes
|
207
|
+
# We _want_ to pass Errno back to caller, which is why we don't
|
208
|
+
# simply call #svok?() here.
|
209
|
+
Util::open_write(svfn('ok')) {true}
|
210
|
+
|
211
|
+
buf = Util::open_read(svfn('status')) do |f|
|
212
|
+
begin
|
213
|
+
f.sysread( StatusBytes::BUFLEN )
|
214
|
+
rescue ::EOFError
|
215
|
+
""
|
216
|
+
end
|
217
|
+
end
|
218
|
+
StatusBytes.new(buf)
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end #-- module Sv
|
224
|
+
end #-- module Sys
|