samhendley-god 0.7.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/History.txt +293 -0
  2. data/Manifest.txt +114 -0
  3. data/README.txt +60 -0
  4. data/Rakefile +35 -0
  5. data/bin/god +128 -0
  6. data/examples/events.god +84 -0
  7. data/examples/gravatar.god +54 -0
  8. data/examples/single.god +66 -0
  9. data/ext/god/extconf.rb +55 -0
  10. data/ext/god/kqueue_handler.c +123 -0
  11. data/ext/god/netlink_handler.c +167 -0
  12. data/init/god +42 -0
  13. data/lib/god.rb +667 -0
  14. data/lib/god/behavior.rb +52 -0
  15. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  16. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  17. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  18. data/lib/god/cli/command.rb +229 -0
  19. data/lib/god/cli/run.rb +176 -0
  20. data/lib/god/cli/version.rb +23 -0
  21. data/lib/god/condition.rb +96 -0
  22. data/lib/god/conditions/always.rb +23 -0
  23. data/lib/god/conditions/complex.rb +86 -0
  24. data/lib/god/conditions/cpu_usage.rb +80 -0
  25. data/lib/god/conditions/degrading_lambda.rb +52 -0
  26. data/lib/god/conditions/disk_usage.rb +27 -0
  27. data/lib/god/conditions/file_mtime.rb +28 -0
  28. data/lib/god/conditions/flapping.rb +128 -0
  29. data/lib/god/conditions/http_response_code.rb +168 -0
  30. data/lib/god/conditions/lambda.rb +25 -0
  31. data/lib/god/conditions/memory_usage.rb +82 -0
  32. data/lib/god/conditions/process_exits.rb +72 -0
  33. data/lib/god/conditions/process_running.rb +74 -0
  34. data/lib/god/conditions/tries.rb +44 -0
  35. data/lib/god/configurable.rb +57 -0
  36. data/lib/god/contact.rb +106 -0
  37. data/lib/god/contacts/campfire.rb +82 -0
  38. data/lib/god/contacts/email.rb +95 -0
  39. data/lib/god/contacts/jabber.rb +65 -0
  40. data/lib/god/contacts/twitter.rb +39 -0
  41. data/lib/god/contacts/webhook.rb +47 -0
  42. data/lib/god/dependency_graph.rb +41 -0
  43. data/lib/god/diagnostics.rb +37 -0
  44. data/lib/god/driver.rb +206 -0
  45. data/lib/god/errors.rb +24 -0
  46. data/lib/god/event_handler.rb +111 -0
  47. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  48. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  49. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  50. data/lib/god/logger.rb +120 -0
  51. data/lib/god/metric.rb +59 -0
  52. data/lib/god/process.rb +342 -0
  53. data/lib/god/registry.rb +32 -0
  54. data/lib/god/simple_logger.rb +53 -0
  55. data/lib/god/socket.rb +96 -0
  56. data/lib/god/sugar.rb +47 -0
  57. data/lib/god/system/portable_poller.rb +42 -0
  58. data/lib/god/system/process.rb +42 -0
  59. data/lib/god/system/slash_proc_poller.rb +92 -0
  60. data/lib/god/task.rb +491 -0
  61. data/lib/god/timeline.rb +25 -0
  62. data/lib/god/trigger.rb +43 -0
  63. data/lib/god/watch.rb +184 -0
  64. data/test/configs/child_events/child_events.god +44 -0
  65. data/test/configs/child_events/simple_server.rb +3 -0
  66. data/test/configs/child_polls/child_polls.god +37 -0
  67. data/test/configs/child_polls/simple_server.rb +12 -0
  68. data/test/configs/complex/complex.god +59 -0
  69. data/test/configs/complex/simple_server.rb +3 -0
  70. data/test/configs/contact/contact.god +84 -0
  71. data/test/configs/contact/simple_server.rb +3 -0
  72. data/test/configs/daemon_events/daemon_events.god +37 -0
  73. data/test/configs/daemon_events/simple_server.rb +8 -0
  74. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  75. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  76. data/test/configs/daemon_polls/simple_server.rb +6 -0
  77. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  78. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  79. data/test/configs/matias/matias.god +50 -0
  80. data/test/configs/real.rb +59 -0
  81. data/test/configs/running_load/running_load.god +16 -0
  82. data/test/configs/stress/simple_server.rb +3 -0
  83. data/test/configs/stress/stress.god +15 -0
  84. data/test/configs/task/logs/.placeholder +0 -0
  85. data/test/configs/task/task.god +26 -0
  86. data/test/configs/test.rb +61 -0
  87. data/test/helper.rb +151 -0
  88. data/test/suite.rb +6 -0
  89. data/test/test_behavior.rb +21 -0
  90. data/test/test_campfire.rb +41 -0
  91. data/test/test_condition.rb +50 -0
  92. data/test/test_conditions_disk_usage.rb +56 -0
  93. data/test/test_conditions_http_response_code.rb +109 -0
  94. data/test/test_conditions_process_running.rb +44 -0
  95. data/test/test_conditions_tries.rb +67 -0
  96. data/test/test_contact.rb +109 -0
  97. data/test/test_dependency_graph.rb +62 -0
  98. data/test/test_driver.rb +11 -0
  99. data/test/test_email.rb +45 -0
  100. data/test/test_event_handler.rb +80 -0
  101. data/test/test_god.rb +598 -0
  102. data/test/test_handlers_kqueue_handler.rb +16 -0
  103. data/test/test_logger.rb +63 -0
  104. data/test/test_metric.rb +72 -0
  105. data/test/test_process.rb +246 -0
  106. data/test/test_registry.rb +15 -0
  107. data/test/test_socket.rb +42 -0
  108. data/test/test_sugar.rb +42 -0
  109. data/test/test_system_portable_poller.rb +17 -0
  110. data/test/test_system_process.rb +30 -0
  111. data/test/test_task.rb +262 -0
  112. data/test/test_timeline.rb +37 -0
  113. data/test/test_trigger.rb +59 -0
  114. data/test/test_watch.rb +279 -0
  115. metadata +193 -0
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+
4
+ Hoe.new('god', '0.7.13') do |p|
5
+ p.rubyforge_name = 'god'
6
+ p.author = 'Tom Preston-Werner'
7
+ p.email = 'tom@rubyisawesome.com'
8
+ p.url = 'http://god.rubyforge.org/'
9
+ p.summary = 'Like monit, only awesome'
10
+ p.description = "God is an easy to configure, easy to extend monitoring framework written in Ruby."
11
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
12
+ p.spec_extras = {:extensions => ['ext/god/extconf.rb']}
13
+ end
14
+
15
+ desc "Open an irb session preloaded with this library"
16
+ task :console do
17
+ sh "irb -rubygems -r ./lib/god.rb"
18
+ end
19
+
20
+ desc "Upload site to Rubyforge"
21
+ task :site do
22
+ sh "scp -r site/* mojombo@god.rubyforge.org:/var/www/gforge-projects/god"
23
+ end
24
+
25
+ desc "Upload site to Rubyforge"
26
+ task :site_edge do
27
+ sh "scp -r site/* mojombo@god.rubyforge.org:/var/www/gforge-projects/god/edge"
28
+ end
29
+
30
+ desc "Run rcov"
31
+ task :coverage do
32
+ `rm -fr coverage`
33
+ `rcov test/test_*.rb`
34
+ `open coverage/index.html`
35
+ end
data/bin/god ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ STDOUT.sync = true
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
6
+
7
+ require 'rubygems'
8
+ require 'optparse'
9
+ require 'drb'
10
+
11
+ begin
12
+ options = {:daemonize => true, :port => 17165, :syslog => true, :events => true}
13
+
14
+ opts = OptionParser.new do |opts|
15
+ opts.banner = <<-EOF
16
+ Usage:
17
+ Starting:
18
+ god [-c <config file>] [-p <port> | -b] [-P <file>] [-l <file>] [-D]
19
+
20
+ Querying:
21
+ god <command> <argument> [-p <port>]
22
+ god <command> [-p <port>]
23
+ god -v
24
+ god -V (must be run as root to be accurate on Linux)
25
+
26
+ Commands:
27
+ start <task or group name> start task or group
28
+ restart <task or group name> restart task or group
29
+ stop <task or group name> stop task or group
30
+ monitor <task or group name> monitor task or group
31
+ unmonitor <task or group name> unmonitor task or group
32
+ remove <task or group name> remove task or group from god
33
+ load <file> load a config into a running god
34
+ log <task name> show realtime log for given task
35
+ status show status of each task
36
+ signal <task or group name> <sig> signal all matching tasks
37
+ quit stop god
38
+ terminate stop god and all tasks
39
+ check run self diagnostic
40
+
41
+ Options:
42
+ EOF
43
+
44
+ opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
45
+ options[:config] = x
46
+ end
47
+
48
+ opts.on("-pPORT", "--port PORT", "Communications port (default 17165)") do |x|
49
+ options[:port] = x
50
+ end
51
+
52
+ opts.on("-b", "--auto-bind", "Auto-bind to an unused port number") do
53
+ options[:port] = "0"
54
+ end
55
+
56
+ opts.on("-PFILE", "--pid FILE", "Where to write the PID file") do |x|
57
+ options[:pid] = x
58
+ end
59
+
60
+ opts.on("-lFILE", "--log FILE", "Where to write the log file") do |x|
61
+ options[:log] = x
62
+ end
63
+
64
+ opts.on("-D", "--no-daemonize", "Don't daemonize") do
65
+ options[:daemonize] = false
66
+ end
67
+
68
+ opts.on("-v", "--version", "Print the version number and exit") do
69
+ options[:version] = true
70
+ end
71
+
72
+ opts.on("-V", "Print extended version and build information") do
73
+ options[:info] = true
74
+ end
75
+
76
+ opts.on("--log-level LEVEL", "Log level [debug|info|warn|error|fatal]") do |x|
77
+ options[:log_level] = x.to_sym
78
+ end
79
+
80
+ opts.on("--no-syslog", "Disable output to syslog") do
81
+ options[:syslog] = false
82
+ end
83
+
84
+ opts.on("--attach PID", "Quit god when the attached process dies") do |x|
85
+ options[:attach] = x
86
+ end
87
+
88
+ opts.on("--no-events", "Disable the event system") do
89
+ options[:events] = false
90
+ end
91
+
92
+ opts.on("--bleakhouse", "Enable bleakhouse profiling") do
93
+ options[:bleakhouse] = true
94
+ end
95
+ end
96
+
97
+ opts.parse!
98
+
99
+ # validate
100
+ if options[:log_level] && ![:debug, :info, :warn, :error, :fatal].include?(options[:log_level])
101
+ abort("Invalid log level '#{options[:log_level]}'")
102
+ end
103
+
104
+ # dispatch
105
+ if !options[:config] && options[:version]
106
+ require 'god'
107
+ God::CLI::Version.version
108
+ elsif !options[:config] && options[:info]
109
+ require 'god'
110
+ God::EventHandler.load
111
+ God::CLI::Version.version_extended
112
+ elsif !options[:config] && command = ARGV[0]
113
+ require 'god'
114
+ God::EventHandler.load
115
+ God::CLI::Command.new(command, options, ARGV)
116
+ else
117
+ require 'god/cli/run'
118
+ God::CLI::Run.new(options)
119
+ end
120
+ rescue Exception => e
121
+ if e.instance_of?(SystemExit)
122
+ raise
123
+ else
124
+ puts 'Uncaught exception'
125
+ puts e.message
126
+ puts e.backtrace.join("\n")
127
+ end
128
+ end
@@ -0,0 +1,84 @@
1
+ # This example shows how you might keep a local development Rails server up
2
+ # and running on your Mac.
3
+
4
+ # Run with:
5
+ # god -c /path/to/events.god
6
+
7
+ RAILS_ROOT = ENV['GOD_TEST_RAILS_ROOT']
8
+
9
+ %w{3002}.each do |port|
10
+ God.watch do |w|
11
+ w.name = "local-#{port}"
12
+ w.interval = 5.seconds
13
+ w.start = "mongrel_rails start -p #{port} -P #{RAILS_ROOT}/log/mongrel.#{port}.pid -c #{RAILS_ROOT} -d"
14
+ w.stop = "mongrel_rails stop -P #{RAILS_ROOT}/log/mongrel.#{port}.pid -c #{RAILS_ROOT}"
15
+ w.pid_file = File.join(RAILS_ROOT, "log/mongrel.#{port}.pid")
16
+ w.log = File.join(RAILS_ROOT, "log/commands.#{port}.log")
17
+
18
+ # clean pid files before start if necessary
19
+ w.behavior(:clean_pid_file)
20
+
21
+ # determine the state on startup
22
+ w.transition(:init, { true => :up, false => :start }) do |on|
23
+ on.condition(:process_running) do |c|
24
+ c.running = true
25
+ end
26
+ end
27
+
28
+ # determine when process has finished starting
29
+ w.transition([:start, :restart], :up) do |on|
30
+ on.condition(:process_running) do |c|
31
+ c.running = true
32
+ end
33
+
34
+ # failsafe
35
+ on.condition(:tries) do |c|
36
+ c.times = 8
37
+ c.within = 2.minutes
38
+ c.transition = :start
39
+ end
40
+ end
41
+
42
+ # start if process is not running
43
+ w.transition(:up, :start) do |on|
44
+ on.condition(:process_exits)
45
+ end
46
+
47
+ # restart if memory or cpu is too high
48
+ w.transition(:up, :restart) do |on|
49
+ on.condition(:memory_usage) do |c|
50
+ c.interval = 20
51
+ c.above = 50.megabytes
52
+ c.times = [3, 5]
53
+ end
54
+
55
+ on.condition(:cpu_usage) do |c|
56
+ c.interval = 10
57
+ c.above = 10.percent
58
+ c.times = 5
59
+ end
60
+
61
+ on.condition(:http_response_code) do |c|
62
+ c.host = 'localhost'
63
+ c.port = port
64
+ c.path = '/'
65
+ c.code_is = 500
66
+ c.timeout = 10.seconds
67
+ c.times = [3, 5]
68
+ end
69
+ end
70
+
71
+ # lifecycle
72
+ w.lifecycle do |on|
73
+ on.condition(:flapping) do |c|
74
+ c.to_state = [:start, :restart]
75
+ c.times = 5
76
+ c.within = 1.minute
77
+ c.transition = :unmonitored
78
+ c.retry_in = 10.minutes
79
+ c.retry_times = 5
80
+ c.retry_within = 2.hours
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,54 @@
1
+ # run with: god -c /path/to/gravatar.god
2
+ #
3
+ # This is the actual config file used to keep the mongrels of
4
+ # gravatar.com running.
5
+
6
+ RAILS_ROOT = "/Users/tom/dev/gravatar2"
7
+
8
+ %w{8200 8201 8202}.each do |port|
9
+ God.watch do |w|
10
+ w.name = "gravatar2-mongrel-#{port}"
11
+ w.interval = 30.seconds # default
12
+ w.start = "mongrel_rails start -c #{RAILS_ROOT} -p #{port} \
13
+ -P #{RAILS_ROOT}/log/mongrel.#{port}.pid -d"
14
+ w.stop = "mongrel_rails stop -P #{RAILS_ROOT}/log/mongrel.#{port}.pid"
15
+ w.restart = "mongrel_rails restart -P #{RAILS_ROOT}/log/mongrel.#{port}.pid"
16
+ w.start_grace = 10.seconds
17
+ w.restart_grace = 10.seconds
18
+ w.pid_file = File.join(RAILS_ROOT, "log/mongrel.#{port}.pid")
19
+
20
+ w.behavior(:clean_pid_file)
21
+
22
+ w.start_if do |start|
23
+ start.condition(:process_running) do |c|
24
+ c.interval = 5.seconds
25
+ c.running = false
26
+ end
27
+ end
28
+
29
+ w.restart_if do |restart|
30
+ restart.condition(:memory_usage) do |c|
31
+ c.above = 150.megabytes
32
+ c.times = [3, 5] # 3 out of 5 intervals
33
+ end
34
+
35
+ restart.condition(:cpu_usage) do |c|
36
+ c.above = 50.percent
37
+ c.times = 5
38
+ end
39
+ end
40
+
41
+ # lifecycle
42
+ w.lifecycle do |on|
43
+ on.condition(:flapping) do |c|
44
+ c.to_state = [:start, :restart]
45
+ c.times = 5
46
+ c.within = 5.minute
47
+ c.transition = :unmonitored
48
+ c.retry_in = 10.minutes
49
+ c.retry_times = 5
50
+ c.retry_within = 2.hours
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,66 @@
1
+ RAILS_ROOT = "/Users/tom/dev/gravatar2"
2
+
3
+ God.watch do |w|
4
+ w.name = "local-3000"
5
+ w.interval = 5.seconds # default
6
+ w.start = "mongrel_rails start -c #{RAILS_ROOT} -P #{RAILS_ROOT}/log/mongrel.pid -p 3000 -d"
7
+ w.stop = "mongrel_rails stop -P #{RAILS_ROOT}/log/mongrel.pid"
8
+ w.restart = "mongrel_rails restart -P #{RAILS_ROOT}/log/mongrel.pid"
9
+ w.pid_file = File.join(RAILS_ROOT, "log/mongrel.pid")
10
+
11
+ # clean pid files before start if necessary
12
+ w.behavior(:clean_pid_file)
13
+
14
+ # determine the state on startup
15
+ w.transition(:init, { true => :up, false => :start }) do |on|
16
+ on.condition(:process_running) do |c|
17
+ c.running = true
18
+ end
19
+ end
20
+
21
+ # determine when process has finished starting
22
+ w.transition([:start, :restart], :up) do |on|
23
+ on.condition(:process_running) do |c|
24
+ c.running = true
25
+ end
26
+
27
+ # failsafe
28
+ on.condition(:tries) do |c|
29
+ c.times = 5
30
+ c.transition = :start
31
+ end
32
+ end
33
+
34
+ # start if process is not running
35
+ w.transition(:up, :start) do |on|
36
+ on.condition(:process_exits)
37
+ end
38
+
39
+ # restart if memory or cpu is too high
40
+ w.transition(:up, :restart) do |on|
41
+ on.condition(:memory_usage) do |c|
42
+ c.interval = 20
43
+ c.above = 50.megabytes
44
+ c.times = [3, 5]
45
+ end
46
+
47
+ on.condition(:cpu_usage) do |c|
48
+ c.interval = 10
49
+ c.above = 10.percent
50
+ c.times = [3, 5]
51
+ end
52
+ end
53
+
54
+ # lifecycle
55
+ w.lifecycle do |on|
56
+ on.condition(:flapping) do |c|
57
+ c.to_state = [:start, :restart]
58
+ c.times = 5
59
+ c.within = 5.minute
60
+ c.transition = :unmonitored
61
+ c.retry_in = 10.minutes
62
+ c.retry_times = 5
63
+ c.retry_within = 2.hours
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,55 @@
1
+ require 'mkmf'
2
+
3
+ fail = false
4
+
5
+ def create_dummy_makefile
6
+ File.open("Makefile", 'w') do |f|
7
+ f.puts "all:"
8
+ f.puts "install:"
9
+ end
10
+ end
11
+
12
+ case RUBY_PLATFORM
13
+ when /bsd/i, /darwin/i
14
+ unless have_header('sys/event.h')
15
+ puts
16
+ puts "Missing 'sys/event.h' header"
17
+ fail = true
18
+ end
19
+
20
+ if fail
21
+ puts
22
+ puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
23
+ create_dummy_makefile
24
+ else
25
+ create_makefile 'kqueue_handler_ext'
26
+ end
27
+ when /linux/i
28
+ unless have_header('linux/netlink.h')
29
+ puts
30
+ puts "Missing 'linux/netlink.h' header(s)"
31
+ puts "You may need to install a header package for your system"
32
+ fail = true
33
+ end
34
+
35
+ unless have_header('linux/connector.h') && have_header('linux/cn_proc.h')
36
+ puts
37
+ puts "Missing 'linux/connector.h', or 'linux/cn_proc.h' header(s)"
38
+ puts "These are only available in Linux kernel 2.6.15 and later (run `uname -a` to find yours)"
39
+ puts "You may need to install a header package for your system"
40
+ fail = true
41
+ end
42
+
43
+ if fail
44
+ puts
45
+ puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
46
+ create_dummy_makefile
47
+ else
48
+ create_makefile 'netlink_handler_ext'
49
+ end
50
+ else
51
+ puts
52
+ puts "Unsupported platform '#{RUBY_PLATFORM}'. Supported platforms are BSD, DARWIN, and LINUX."
53
+ puts "Your god installation will not support event conditions."
54
+ create_dummy_makefile
55
+ end
@@ -0,0 +1,123 @@
1
+ #if defined(__FreeBSD__) || defined(__APPLE__)
2
+
3
+ #include <ruby.h>
4
+ #include <sys/event.h>
5
+ #include <sys/time.h>
6
+ #include <errno.h>
7
+
8
+ static VALUE mGod;
9
+ static VALUE cKQueueHandler;
10
+ static VALUE cEventHandler;
11
+
12
+ static ID proc_exit;
13
+ static ID proc_fork;
14
+ static ID m_call;
15
+ static ID m_size;
16
+ static ID m_deregister;
17
+
18
+ static int kq;
19
+ int num_events;
20
+
21
+ #define NUM_EVENTS FIX2INT(rb_funcall(rb_cv_get(cEventHandler, "@@actions"), m_size, 0))
22
+
23
+ VALUE
24
+ kqh_event_mask(VALUE klass, VALUE sym)
25
+ {
26
+ ID id = SYM2ID(sym);
27
+ if (proc_exit == id) {
28
+ return UINT2NUM(NOTE_EXIT);
29
+ } else if (proc_fork == id) {
30
+ return UINT2NUM(NOTE_FORK);
31
+ } else {
32
+ rb_raise(rb_eNotImpError, "Event `%s` not implemented", rb_id2name(id));
33
+ }
34
+
35
+ return Qnil;
36
+ }
37
+
38
+
39
+ VALUE
40
+ kqh_monitor_process(VALUE klass, VALUE pid, VALUE mask)
41
+ {
42
+ struct kevent new_event;
43
+ ID event;
44
+
45
+ u_int fflags = NUM2UINT(mask);
46
+
47
+ EV_SET(&new_event, FIX2UINT(pid), EVFILT_PROC,
48
+ EV_ADD | EV_ENABLE, fflags, 0, 0);
49
+
50
+ if (-1 == kevent(kq, &new_event, 1, NULL, 0, NULL)) {
51
+ rb_raise(rb_eStandardError, strerror(errno));
52
+ }
53
+
54
+ num_events = NUM_EVENTS;
55
+
56
+ return Qnil;
57
+ }
58
+
59
+ VALUE
60
+ kqh_handle_events()
61
+ {
62
+ int nevents, i, num_to_fetch;
63
+ struct kevent *events;
64
+ fd_set read_set;
65
+
66
+ FD_ZERO(&read_set);
67
+ FD_SET(kq, &read_set);
68
+
69
+ // Don't actually run this method until we've got an event
70
+ rb_thread_select(kq + 1, &read_set, NULL, NULL, NULL);
71
+
72
+ // Grabbing num_events once for thread safety
73
+ num_to_fetch = num_events;
74
+ events = (struct kevent*)malloc(num_to_fetch * sizeof(struct kevent));
75
+
76
+ if (NULL == events) {
77
+ rb_raise(rb_eStandardError, strerror(errno));
78
+ }
79
+
80
+ nevents = kevent(kq, NULL, 0, events, num_to_fetch, NULL);
81
+
82
+ if (-1 == nevents) {
83
+ free(events);
84
+ rb_raise(rb_eStandardError, strerror(errno));
85
+ } else {
86
+ for (i = 0; i < nevents; i++) {
87
+ if (events[i].fflags & NOTE_EXIT) {
88
+ rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_exit));
89
+ } else if (events[i].fflags & NOTE_FORK) {
90
+ rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_fork));
91
+ }
92
+ }
93
+ }
94
+
95
+ free(events);
96
+
97
+ return INT2FIX(nevents);
98
+ }
99
+
100
+ void
101
+ Init_kqueue_handler_ext()
102
+ {
103
+ kq = kqueue();
104
+
105
+ if (kq == -1) {
106
+ rb_raise(rb_eStandardError, "kqueue initilization failed");
107
+ }
108
+
109
+ proc_exit = rb_intern("proc_exit");
110
+ proc_fork = rb_intern("proc_fork");
111
+ m_call = rb_intern("call");
112
+ m_size = rb_intern("size");
113
+ m_deregister = rb_intern("deregister");
114
+
115
+ mGod = rb_const_get(rb_cObject, rb_intern("God"));
116
+ cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
117
+ cKQueueHandler = rb_define_class_under(mGod, "KQueueHandler", rb_cObject);
118
+ rb_define_singleton_method(cKQueueHandler, "monitor_process", kqh_monitor_process, 2);
119
+ rb_define_singleton_method(cKQueueHandler, "handle_events", kqh_handle_events, 0);
120
+ rb_define_singleton_method(cKQueueHandler, "event_mask", kqh_event_mask, 1);
121
+ }
122
+
123
+ #endif