svdir 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/CHANGELOG +29 -0
  2. data/LICENSE +19 -0
  3. data/README +67 -0
  4. data/Rakefile +66 -0
  5. data/example/svctl +117 -0
  6. data/lib/sys/sv/statusbytes.rb +53 -0
  7. data/lib/sys/sv/svdir.rb +224 -0
  8. data/lib/sys/sv/util.rb +36 -0
  9. data/test/fixtures/PrefabSvDir.rb +22 -0
  10. data/test/fixtures/TempSvDir.rb +62 -0
  11. data/test/services/corrupt-normd/down +0 -0
  12. data/test/services/corrupt-normd/supervise/ok +0 -0
  13. data/test/services/corrupt-normd/supervise/status +0 -0
  14. data/test/services/corrupt-normu/supervise/ok +0 -0
  15. data/test/services/corrupt-normu/supervise/status +0 -0
  16. data/test/services/corrupt-zero-normd/down +0 -0
  17. data/test/services/corrupt-zero-normd/supervise/ok +0 -0
  18. data/test/services/corrupt-zero-normd/supervise/status +0 -0
  19. data/test/services/corrupt-zero-normu/supervise/ok +0 -0
  20. data/test/services/corrupt-zero-normu/supervise/status +0 -0
  21. data/test/services/down-0-normd-nowant/down +0 -0
  22. data/test/services/down-0-normd-nowant/supervise/ok +0 -0
  23. data/test/services/down-0-normd-nowant/supervise/status +0 -0
  24. data/test/services/down-0-normu-nowant/supervise/ok +0 -0
  25. data/test/services/down-0-normu-nowant/supervise/status +0 -0
  26. data/test/services/up-12526-normu-wantd/supervise/ok +0 -0
  27. data/test/services/up-12526-normu-wantd/supervise/status +0 -0
  28. data/test/services/up-12581-normd-wantd-paused/down +0 -0
  29. data/test/services/up-12581-normd-wantd-paused/supervise/ok +0 -0
  30. data/test/services/up-12581-normd-wantd-paused/supervise/status +0 -0
  31. data/test/services/up-12581-normu-wantd-paused/supervise/ok +0 -0
  32. data/test/services/up-12581-normu-wantd-paused/supervise/status +0 -0
  33. data/test/services/up-12816-normd-nowant/down +0 -0
  34. data/test/services/up-12816-normd-nowant/supervise/ok +0 -0
  35. data/test/services/up-12816-normd-nowant/supervise/status +0 -0
  36. data/test/services/up-12816-normu-nowant/supervise/ok +0 -0
  37. data/test/services/up-12816-normu-nowant/supervise/status +0 -0
  38. data/test/services/up-12868-normd-wantu-paused/down +0 -0
  39. data/test/services/up-12868-normd-wantu-paused/supervise/ok +0 -0
  40. data/test/services/up-12868-normd-wantu-paused/supervise/status +0 -0
  41. data/test/services/up-12868-normd-wantu/down +0 -0
  42. data/test/services/up-12868-normd-wantu/supervise/ok +0 -0
  43. data/test/services/up-12868-normd-wantu/supervise/status +0 -0
  44. data/test/services/up-12868-normu-wantu-paused/supervise/ok +0 -0
  45. data/test/services/up-12868-normu-wantu-paused/supervise/status +0 -0
  46. data/test/services/up-12868-normu-wantu/supervise/ok +0 -0
  47. data/test/services/up-12868-normu-wantu/supervise/status +0 -0
  48. data/test/services/up-16464-normd-wantd/down +0 -0
  49. data/test/services/up-16464-normd-wantd/supervise/ok +0 -0
  50. data/test/services/up-16464-normd-wantd/supervise/status +0 -0
  51. data/test/testbase.rb +30 -0
  52. data/test/ts_corrupt.rb +133 -0
  53. data/test/ts_nosupervisor.rb +81 -0
  54. data/test/ts_signal.rb +128 -0
  55. data/test/ts_svstat.rb +548 -0
  56. metadata +117 -0
@@ -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")
@@ -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
@@ -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
@@ -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