pazuzu 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
1
+ module Pazuzu
2
+
3
+ # Runs the supervisor daemon from the command line.
4
+ class SupervisorRunner
5
+
6
+ def initialize
7
+ @run_as_daemon = false
8
+ end
9
+
10
+ def run!(arguments = [])
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{prog_name} [OPTIONS]"
13
+ opts.separator ""
14
+ opts.on("-d", "--daemon", 'Run as daemon') do
15
+ @run_as_daemon = true
16
+ end
17
+ opts.on("-p PATH", "--pid", "Store pid in PATH (defaults to #{DEFAULT_PID_PATH})") do |value|
18
+ @pid_path = File.expand_path(value)
19
+ end
20
+ opts.on("-c FILE", "--config FILE", "Read configuration from FILE (defaults to #{DEFAULT_CONFIG_PATH})") do |value|
21
+ @config_path = File.expand_path(value)
22
+ end
23
+ opts.on("-h", "--help", "Show this help.") do
24
+ puts opts
25
+ exit
26
+ end
27
+ opts.parse!(arguments)
28
+ end
29
+
30
+ if @run_as_daemon
31
+ daemonize!
32
+ else
33
+ execute!
34
+ end
35
+ rescue => e
36
+ abort "#{prog_name}: #{e.message}"
37
+ end
38
+
39
+ private
40
+
41
+ DEFAULT_PID_PATH = '/var/run/pazuzud.pid'.freeze
42
+ DEFAULT_CONFIG_PATH = '/etc/pazuzu/pazuzu.conf'.freeze
43
+
44
+ def execute!
45
+ @config_path ||= DEFAULT_CONFIG_PATH
46
+ with_pid do
47
+ @supervisor = Pazuzu::Supervisor.new(:config_path => @config_path)
48
+ Signal.trap 'HUP' do
49
+ @supervisor.load_configuration!
50
+ end
51
+ begin
52
+ @supervisor.run!
53
+ rescue SystemExit
54
+ end
55
+ end
56
+ end
57
+
58
+ def daemonize!(&block)
59
+ return Process.fork {
60
+ Process.setsid
61
+ 0.upto(255) do |n|
62
+ File.for_fd(n, "r").close rescue nil
63
+ end
64
+
65
+ File.umask(27)
66
+ Dir.chdir('/')
67
+ $stdin.reopen("/dev/null", 'r')
68
+ $stdout.reopen("/dev/null", 'w')
69
+ $stderr.reopen("/dev/null", 'w')
70
+
71
+ execute!
72
+ }
73
+ end
74
+
75
+ def with_pid(&block)
76
+ path = @pid_path
77
+ path ||= DEFAULT_PID_PATH
78
+
79
+ File.open(path, 'w') do |file|
80
+ file << Process.pid
81
+ end
82
+ begin
83
+ yield
84
+ ensure
85
+ File.delete(path) rescue nil
86
+ end
87
+ end
88
+
89
+ def prog_name
90
+ File.basename($0)
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,46 @@
1
+ module Pazuzu
2
+ module Utility
3
+
4
+ # Logger which annotates all log messages.
5
+ class AnnotatedLogger < SimpleDelegator
6
+
7
+ def initialize(logger, prefix)
8
+ super(logger)
9
+ @prefix = prefix
10
+ end
11
+
12
+ def debug(message = nil, &block)
13
+ __getobj__.debug(format_message(message), &block)
14
+ end
15
+
16
+ def info(message = nil, &block)
17
+ __getobj__.info(format_message(message), &block)
18
+ end
19
+
20
+ def warn(message = nil, &block)
21
+ __getobj__.warn(format_message(message), &block)
22
+ end
23
+
24
+ def error(message = nil, &block)
25
+ __getobj__.error(format_message(message), &block)
26
+ end
27
+
28
+ def add(severity, message = nil, progname = nil, &block)
29
+ __getobj__.add(severity, format_message(message), progname, &block)
30
+ end
31
+
32
+ private
33
+
34
+ def format_message(message)
35
+ if @prefix.is_a?(Proc)
36
+ prefix = @prefix.call
37
+ else
38
+ prefix = @prefix
39
+ end
40
+ "[#{prefix}] #{message}"
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,60 @@
1
+ module Pazuzu
2
+ module Utility
3
+
4
+ class OutputTailer
5
+
6
+ def initialize(options = {})
7
+ @limit = options[:limit] || 1000
8
+ @callback = options[:on_line] || proc { }
9
+ @mutex = Mutex.new
10
+ @buffer = ''
11
+ @entries = []
12
+ end
13
+
14
+ def open
15
+ close
16
+ @stream, writeable_stream = IO.pipe
17
+ @thread = Thread.start { tail! }
18
+ return writeable_stream
19
+ end
20
+
21
+ def close
22
+ if @stream
23
+ @stream.close rescue nil
24
+ @stream = nil
25
+ end
26
+ if @thread
27
+ @thread.terminate
28
+ @thread = nil
29
+ end
30
+ end
31
+
32
+ def tail!
33
+ while @stream
34
+ data = @stream.readpartial(4096)
35
+ @mutex.synchronize do
36
+ @buffer << data
37
+ while @buffer =~ /\A([^\r\n]*)\r?\n(.*)/
38
+ @buffer = $2
39
+ add_line!($1)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ attr_reader :entries
46
+ attr_reader :limit
47
+
48
+ private
49
+
50
+ def add_line!(line)
51
+ pair = [Time.now, line]
52
+ @entries << pair
53
+ @entries.shift while @entries.length > @limit
54
+ @callback.call(pair)
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ module Pazuzu
2
+ module Utility
3
+
4
+ module ProcessSpawning
5
+
6
+ def self.prepare_child_process!
7
+ # Close file handles
8
+ 3.upto(255) do |n|
9
+ File.for_fd(n, 'r').close rescue nil
10
+ end
11
+
12
+ # Reopen standard file handles
13
+ $stdin.reopen("/dev/null", 'r')
14
+ $stdout.reopen("/dev/null", 'w')
15
+ $stderr.reopen("/dev/null", 'w')
16
+
17
+ # Ignore SIGHUP
18
+ Signal.trap("HUP") { }
19
+ end
20
+
21
+ def self.set_user_and_group!(user, group)
22
+ current_uid, current_gid = Process.euid, Process.egid
23
+
24
+ new_uid = Etc.getpwnam(user).uid if user
25
+ new_uid ||= current_uid
26
+ new_gid = Etc.getgrnam(group).gid if group
27
+ new_gid ||= current_gid
28
+ if new_uid != current_uid or new_gid != current_gid
29
+ user_name = user
30
+ user_name ||= Etc.getpwnam(new_uid).name
31
+ Process.initgroups(user_name, new_gid)
32
+ Process::GID.change_privilege(new_gid)
33
+ Process::UID.change_privilege(new_uid)
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,68 @@
1
+ module Pazuzu
2
+ module Utility
3
+
4
+ # Simple limiter that can be used to limit the rate of processing. Uses
5
+ # a short time window to calculate current rate.
6
+ class RateLimiter
7
+
8
+ # Initialize limiter with +max_hertz+ as the maximum frequency per second,
9
+ # and +window_seconds+ as the number of seconds to calculate frequency
10
+ # based on.
11
+ def initialize(max_hertz, window_seconds = 3.0)
12
+ @max_hertz = max_hertz
13
+ @history_seconds = window_seconds
14
+ @window = []
15
+ end
16
+
17
+ # Returns true if current rate exceeds limit.
18
+ def limited?
19
+ iterate
20
+ rate > @max_hertz
21
+ end
22
+
23
+ # Count a cycle. If the current rate exceeds the permitted limit, it will
24
+ # sleep until the rate goes below the limit. Returns true if not currently
25
+ # rate-limited, otherwise false.
26
+ def count!
27
+ iterate
28
+ limited = false
29
+ @window[-1][1] += 1
30
+ while rate > @max_hertz
31
+ sleep(0.5)
32
+ limited = true
33
+ iterate
34
+ end
35
+ !limited
36
+ end
37
+
38
+ # Returns current rate, in hertz.
39
+ def rate
40
+ rate = 0
41
+ window = @window
42
+ unless window.empty?
43
+ range = window[-1][0] - window[0][0]
44
+ if range > 0
45
+ total = window.inject(0.0) { |sum, (t, count)| sum + count }
46
+ rate = total / range if total >= @max_hertz
47
+ end
48
+ end
49
+ rate
50
+ end
51
+
52
+ private
53
+
54
+ def iterate
55
+ now = Time.now.to_f.floor
56
+ window = @window
57
+ top = window[-1]
58
+ if !top or top[0] != now
59
+ window << [now, 0]
60
+ end
61
+ time_threshold = now - @history_seconds
62
+ window.delete_if { |t, count| t < time_threshold }
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,120 @@
1
+ module Pazuzu
2
+ module Utility
3
+
4
+ # Mixin that provides a state machine for running state.
5
+ module Runnable
6
+
7
+ def initialize
8
+ _self = self
9
+ @started_at = nil
10
+ @stopped_at = nil
11
+ @state_condition = ConditionVariable.new
12
+ @mutex = Mutex.new
13
+ @runnable_state = Statemachine.build do
14
+ state :stopped do
15
+ event :start!, :starting
16
+ on_entry :enter_stopped
17
+ end
18
+ state :starting do
19
+ event :started!, :running
20
+ event :failed!, :failed
21
+ event :stop!, :stopping
22
+ on_entry :enter_starting
23
+ end
24
+ state :running do
25
+ event :stop!, :stopping
26
+ event :starting!, :starting
27
+ on_entry :enter_started
28
+ end
29
+ state :stopping do
30
+ event :stopped!, :stopped
31
+ on_entry :enter_stopping
32
+ end
33
+ state :failed do
34
+ on_entry :enter_failed
35
+ end
36
+ context _self
37
+ end
38
+ end
39
+
40
+ def start!
41
+ if [:stopped, :failed].include?(@runnable_state.state)
42
+ @runnable_state.start!
43
+ end
44
+ end
45
+
46
+ def stop!
47
+ if [:starting, :running].include?(@runnable_state.state)
48
+ @runnable_state.stop!
49
+ end
50
+ end
51
+
52
+ def run_state
53
+ @runnable_state.state
54
+ end
55
+
56
+ def wait_for_state_change!
57
+ @mutex.synchronize { @state_condition.wait(@mutex) }
58
+ end
59
+
60
+ attr_reader :started_at
61
+ attr_reader :stopped_at
62
+
63
+ protected
64
+
65
+ attr_reader :runnable_state
66
+
67
+ def on_starting
68
+ end
69
+
70
+ def on_started
71
+ end
72
+
73
+ def on_stopping
74
+ end
75
+
76
+ def on_stopped
77
+ end
78
+
79
+ def on_failed
80
+ end
81
+
82
+ private
83
+
84
+ def signal_state_change!
85
+ @mutex.synchronize { @state_condition.signal }
86
+ end
87
+
88
+ def enter_starting
89
+ signal_state_change!
90
+ on_starting
91
+ end
92
+
93
+ def enter_started
94
+ signal_state_change!
95
+ @stopped_at = nil
96
+ @started_at = Time.now
97
+ on_started
98
+ end
99
+
100
+ def enter_stopping
101
+ signal_state_change!
102
+ on_stopping
103
+ end
104
+
105
+ def enter_stopped
106
+ signal_state_change!
107
+ @stopped_at = Time.now
108
+ @started_at = nil
109
+ on_stopped
110
+ end
111
+
112
+ def enter_failed
113
+ signal_state_change!
114
+ on_failed
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,62 @@
1
+ module Pazuzu
2
+ module Utility
3
+
4
+ # Runnable which manages a pool of child runnables that reflect its
5
+ # parent's running state.
6
+ class RunnablePool
7
+
8
+ include Runnable
9
+
10
+ def initialize
11
+ super
12
+ @children_mutex = Mutex.new
13
+ @children = Set.new
14
+ end
15
+
16
+ def length
17
+ children.length
18
+ end
19
+
20
+ def register(child)
21
+ raise ArgumentError, 'Child must be runnable' unless child.is_a?(Runnable)
22
+ @children_mutex.synchronize do
23
+ @children.add(child)
24
+ end
25
+ if [:running, :starting].include?(run_state)
26
+ child.start!
27
+ end
28
+ end
29
+
30
+ def unregister(child)
31
+ if [:running, :starting].include?(child.run_state)
32
+ child.stop!
33
+ end
34
+ @children_mutex.synchronize do
35
+ @children.delete(child)
36
+ end
37
+ end
38
+
39
+ def children
40
+ @children_mutex.synchronize { @children.to_a }
41
+ end
42
+
43
+ protected
44
+
45
+ def on_starting
46
+ @children_mutex.synchronize { @children.dup }.each do |child|
47
+ child.start!
48
+ end
49
+ runnable_state.started!
50
+ end
51
+
52
+ def on_stopping
53
+ @children_mutex.synchronize { @children.dup }.each do |child|
54
+ child.stop!
55
+ end
56
+ runnable_state.stopped!
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end