watchmonkey_cli 1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +45 -0
  6. data/Rakefile +1 -0
  7. data/VERSION +1 -0
  8. data/bin/watchmonkey +8 -0
  9. data/bin/watchmonkey.sh +14 -0
  10. data/lib/watchmonkey_cli/application/colorize.rb +22 -0
  11. data/lib/watchmonkey_cli/application/configuration.rb +55 -0
  12. data/lib/watchmonkey_cli/application/configuration.tpl +142 -0
  13. data/lib/watchmonkey_cli/application/dispatch.rb +86 -0
  14. data/lib/watchmonkey_cli/application.rb +255 -0
  15. data/lib/watchmonkey_cli/checker.rb +228 -0
  16. data/lib/watchmonkey_cli/checkers/ftp_availability.rb +22 -0
  17. data/lib/watchmonkey_cli/checkers/mysql_replication.rb +57 -0
  18. data/lib/watchmonkey_cli/checkers/ssl_expiration.rb +83 -0
  19. data/lib/watchmonkey_cli/checkers/unix_defaults.rb +40 -0
  20. data/lib/watchmonkey_cli/checkers/unix_df.rb +41 -0
  21. data/lib/watchmonkey_cli/checkers/unix_file_exists.rb +25 -0
  22. data/lib/watchmonkey_cli/checkers/unix_load.rb +30 -0
  23. data/lib/watchmonkey_cli/checkers/unix_mdadm.rb +60 -0
  24. data/lib/watchmonkey_cli/checkers/unix_memory.rb +37 -0
  25. data/lib/watchmonkey_cli/checkers/www_availability.rb +54 -0
  26. data/lib/watchmonkey_cli/helpers.rb +19 -0
  27. data/lib/watchmonkey_cli/hooks/platypus.rb +38 -0
  28. data/lib/watchmonkey_cli/hooks/requeue.rb +106 -0
  29. data/lib/watchmonkey_cli/loopback_connection.rb +36 -0
  30. data/lib/watchmonkey_cli/ssh_connection.rb +50 -0
  31. data/lib/watchmonkey_cli/version.rb +4 -0
  32. data/lib/watchmonkey_cli.rb +41 -0
  33. data/watchmonkey_cli.gemspec +27 -0
  34. metadata +163 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 553a66c3a760e64c80ac8af012b7119721aaf521
4
+ data.tar.gz: fec50a69b3321cfd2c5d38b9e78d65218471e5bd
5
+ SHA512:
6
+ metadata.gz: ca30f9f9e6702f400c3d05bdf81fca702adc04b52126ad15d71c21472a30d5076d761f7a9877120a54768fd0091934856882901b13891758f9d2156e44e80e88
7
+ data.tar.gz: 95df3dba388da9fddc6fd44bf7c9ff6a38480c42db424de3a75307a0949537da4d60d7f3400aac3970eeb43e9fd8f4049ae94be267394d926f0c8c3d51c20449
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dle.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016, Sven Pachnit aka. 2called-chaos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Watchmonkey CLI
2
+
3
+ Coming soon.
4
+
5
+ ---
6
+
7
+ ## Help
8
+ If you need help or have problems [open an issue](https://github.com/2called-chaos/watchmonkey_cli/issues/new).
9
+
10
+
11
+ ## Features
12
+ * Colors!
13
+
14
+
15
+
16
+ ## Requirements
17
+ * Ruby >= 2.0
18
+ * Unixoid OS (such as Ubuntu/Debian, OS X, maybe others) or Windows 7/8 (not recommended)
19
+ * something you want to monitor
20
+
21
+
22
+
23
+ ## Installation
24
+ * `gem install watchmonkey_cli`
25
+ * `watchmonkey --generate-config [name=default]`
26
+
27
+
28
+ ## Deactivate configs
29
+ If you want to deactivate configs entirely just rename the file to start with two underscores (e.g.: `__example.rb`).
30
+
31
+
32
+ ## Contributing
33
+ Contributions are very welcome! Either report errors, bugs and propose features or directly submit code:
34
+
35
+ 1. Fork it
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create new Pull Request
40
+
41
+
42
+
43
+ ## Legal
44
+ * © 2016, Sven Pachnit (www.bmonkeys.net)
45
+ * watchmonkey_cli is licensed under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.4
data/bin/watchmonkey ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require "watchmonkey_cli"
3
+ begin
4
+ WatchmonkeyCli::Application.dispatch(ENV, ARGV)
5
+ rescue Interrupt
6
+ puts("\n\nInterrupted")
7
+ exit 1
8
+ end
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+
3
+ # That's __FILE__ in BASH :)
4
+ # From: http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
5
+ SOURCE="${BASH_SOURCE[0]}"
6
+ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
7
+ MCLDIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
8
+ SOURCE="$(readlink "$SOURCE")"
9
+ [[ $SOURCE != /* ]] && SOURCE="$MCLDIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
10
+ done
11
+ PROJECT_ROOT="$( cd -P "$( dirname "$SOURCE" )"/.. && pwd )"
12
+
13
+ # Actually run script
14
+ cd $PROJECT_ROOT && bundle exec ruby bin/watchmonkey "$@"
@@ -0,0 +1,22 @@
1
+ module WatchmonkeyCli
2
+ class Application
3
+ module Colorize
4
+ COLORMAP = {
5
+ black: 30,
6
+ red: 31,
7
+ green: 32,
8
+ yellow: 33,
9
+ blue: 34,
10
+ magenta: 35,
11
+ cyan: 36,
12
+ white: 37,
13
+ }
14
+
15
+ def colorize str, color = :yellow
16
+ ccode = COLORMAP[color.to_sym] || raise(ArgumentError, "Unknown color #{color}!")
17
+ @opts[:colorize] ? "\e[#{ccode}m#{str}\e[0m" : "#{str}"
18
+ end
19
+ alias_method :c, :colorize
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ module WatchmonkeyCli
2
+ class Application
3
+ class Configuration
4
+ module AppHelper
5
+ def config_directory
6
+ "#{wm_cfg_path}/configs"
7
+ end
8
+
9
+ def config_files
10
+ Dir["#{config_directory}/**/*.rb"].reject do |file|
11
+ file.gsub(config_directory, "").split("/").any?{|fp| fp.start_with?("__") }
12
+ end
13
+ end
14
+
15
+ def config_filename name = "default"
16
+ "#{config_directory}/#{name}.rb"
17
+ end
18
+
19
+ def load_configs!
20
+ config_files.each {|f| Configuration.new(self, f) }
21
+ end
22
+
23
+ def generate_config name = "default"
24
+ FileUtils.mkdir_p(config_directory)
25
+ File.open(config_filename(name), "w", encoding: "utf-8") do |f|
26
+ f << File.read("#{File.dirname(__FILE__)}/configuration.tpl", encoding: "utf-8")
27
+ end
28
+ end
29
+ end
30
+
31
+ def initialize app, file
32
+ @app = app
33
+ @file = file
34
+ begin
35
+ eval File.read(file, encoding: "utf-8"), binding, file
36
+ rescue
37
+ app.error "Invalid config file #{file}"
38
+ raise
39
+ end
40
+ end
41
+
42
+ def ssh_connection name, opts = {}, &b
43
+ @app.fetch_connection(:ssh, name, opts, &b)
44
+ end
45
+
46
+ def method_missing meth, *args, &block
47
+ if c = @app.checkers[meth.to_s]
48
+ c.enqueue(*args)
49
+ else
50
+ super
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,142 @@
1
+ # This is a Ruby file!
2
+
3
+ # =================================
4
+ # = Step 1: Setup SSH connections =
5
+ # =================================
6
+
7
+ # Synopsis: ssh_connection <name> <net-ssh options>
8
+ # For a list of options see:
9
+ # http://net-ssh.github.io/net-ssh/Net/SSH.html#method-c-start
10
+ # The default options are { config: false }
11
+ ssh_connection :my_server, host_name: "example.com", user: "wheel", password: "secur3"
12
+
13
+ # Key authentication (if you don't specify any keys the default locations might be used)
14
+ ssh_connection :my_server, host_name: "example.com", user: "wheel", keys_only: true, keys: ["/home/itsme/.ssh/id_rsa"]
15
+
16
+ # Therefore you might get away with just
17
+ ssh_connection :my_server, host_name: "example.com", user: "wheel"
18
+
19
+ # There are also two shortcuts you can use...
20
+ ssh_connection :my_server, "wheel@example.com" # no additional options possible
21
+ ssh_connection :my_server, host: "wheel@example.com", port: 23 # additional options possible
22
+
23
+
24
+
25
+
26
+ # ==========================
27
+ # = Step 2: Monitor stuff! =
28
+ # ==========================
29
+
30
+
31
+ # -----
32
+ # SSL expiration
33
+ # -----
34
+ # Check if a SSL certificate is about to expire.
35
+ # Default threshold is 1.month
36
+ ssl_expiration "https://example.com", threshold: 3.months
37
+
38
+
39
+ # -----
40
+ # WWW availability
41
+ # -----
42
+ # Check if a website is reachable and responses properly.
43
+ # Available options:
44
+ #
45
+ # status HTTP status code or array of status codes
46
+ # body String (include check) or regular expression
47
+ # headers { header => value }
48
+ # * keys are lowercased!
49
+ # * value might be string (equal check) or regular expression
50
+ #
51
+ # Note: If page is https and ssl_expiration is not false
52
+ # SSL expiration will automatically be checked.
53
+ # You can pass options by setting ssl_expiration to a Hash.
54
+
55
+ www_availability "http://example.com", status: 200, body: "<title>Example.com</title>", headers: { "content-type" => "text/html; charset=utf-8" }
56
+
57
+ # SSL expiration
58
+ www_availability "https://example.com", ssl_expiration: false
59
+ www_availability "https://example.com", ssl_expiration: { threshold: 4.weeks }
60
+
61
+
62
+ # -----
63
+ # FTP availability
64
+ # -----
65
+ # Login to an FTP account via net/ftp to check it's functionality.
66
+ # Just a port check is not enough!
67
+ ftp_availability "ftp.example.com", user: "somebody", password: "thatiusedtoknow"
68
+
69
+
70
+ # -----
71
+ # MySQL replication
72
+ # -----
73
+ # Check the health of a MySQL replication.
74
+ # Host might be :local/false/nil which will test locally (without SSH)
75
+ # Available options: user(root), password, host(127.0.0.1), port(3306), sbm_threshold(60)
76
+ # SBM refers to "Seconds Behind Master"
77
+ mysql_replication :my_server, user: "replication_user", password: "pushit"
78
+
79
+
80
+ # -----
81
+ # *nix file_exist
82
+ # -----
83
+ # Check if a file exists or not.
84
+ # Host might be :local/false/nil which will test locally (without SSH)
85
+ # You can change the default message (The file ... does not exist) with the message option.
86
+ unix_file_exists :my_server, "/etc/passwd", message: "There is no passwd, spooky!"
87
+
88
+
89
+ # -----
90
+ # *nix df
91
+ # -----
92
+ # Checks if disks are running low on free space.
93
+ # Host might be :local/false/nil which will test locally (without SSH)
94
+ # Available options: min_percent(25)
95
+ unix_df :my_server
96
+ unix_df :my_server, min_percent: 50
97
+
98
+
99
+ # -----
100
+ # *nix memory
101
+ # -----
102
+ # Checks if memory is running low.
103
+ # Host might be :local/false/nil which will test locally (without SSH)
104
+ # Available options: min_percent(25)
105
+ unix_memory :my_server
106
+ unix_memory :my_server, min_percent: 50
107
+
108
+
109
+ # -----
110
+ # *nix load
111
+ # -----
112
+ # Checks if system load is to high.
113
+ # Host might be :local/false/nil which will test locally (without SSH)
114
+ # Available options: limits([4, 2, 1.5])
115
+ unix_load :my_server
116
+ unix_load :my_server, limits: [3, 2, 1]
117
+
118
+
119
+ # -----
120
+ # *nix mdadm
121
+ # -----
122
+ # Checks if mdadm raids are intact or checking.
123
+ # Host might be :local/false/nil which will test locally (without SSH)
124
+ # Available options: log_checking(true)
125
+ unix_mdadm :my_server
126
+ unix_mdadm :my_server, log_checking: false
127
+
128
+
129
+ # -----
130
+ # *nix defaults
131
+ # -----
132
+ # Combines unix_load, unix_memory, unix_df and unix_mdadm.
133
+ # You can pass options or disable individual checkers by passing
134
+ # a hash whose keys are named after checkers.
135
+ unix_defaults :my_server
136
+ unix_defaults :my_server, unix_mdadm: false, unix_df: { min_percent: 10 }
137
+
138
+ # There are also the following shortcuts:
139
+ unix_defaults :my_server, load: [1, 2, 3] # Array(3) or false
140
+ unix_defaults :my_server, memory_min: 10 # Fixnum or false
141
+ unix_defaults :my_server, df_min: 10 # Fixnum or false
142
+ unix_defaults :my_server, mdadm: false # true or false
@@ -0,0 +1,86 @@
1
+ module WatchmonkeyCli
2
+ class Application
3
+ module Dispatch
4
+ def dispatch action = (@opts[:dispatch] || :help)
5
+ if respond_to?("dispatch_#{action}")
6
+ send("dispatch_#{action}")
7
+ else
8
+ abort("unknown action #{action}", 1)
9
+ end
10
+ end
11
+
12
+ def dispatch_help
13
+ puts @optparse.to_s
14
+ end
15
+
16
+ def dispatch_generate_config
17
+ cfg_name = @opts[:config_name] || "default"
18
+ cfg_file = config_filename(cfg_name)
19
+ puts c("Generating example config `#{cfg_name}'")
20
+ if File.exist?(cfg_file)
21
+ abort "Conflict, file already exists: #{cfg_file}", 1
22
+ else
23
+ generate_config(cfg_name)
24
+ puts c("Writing #{cfg_file}...", :green)
25
+ end
26
+ end
27
+
28
+ def dispatch_index
29
+ Thread.abort_on_exception = true
30
+ trap_signals
31
+ init_checkers!
32
+ load_configs!
33
+ dump_and_exit! if @opts[:dump]
34
+ start_checkers!
35
+ @running = true
36
+ spawn_threads_and_run!
37
+ @threads.each(&:join)
38
+ # puts config_directory
39
+ # puts config_files.inspect
40
+ ensure
41
+ @running = false
42
+ stop_checkers!
43
+ close_connections!
44
+ release_signals
45
+ end
46
+
47
+ def dispatch_info
48
+ your_version = Gem::Version.new(WatchmonkeyCli::VERSION)
49
+ puts c ""
50
+ puts c(" Your version: ", :yellow) << c("#{your_version}", :magenta)
51
+
52
+ print c(" Current version: ", :yellow)
53
+ if @opts[:check_for_updates]
54
+ require "net/http"
55
+ print c("checking...", :blue)
56
+
57
+ begin
58
+ current_version = Gem::Version.new Net::HTTP.get_response(URI.parse(WatchmonkeyCli::UPDATE_URL)).body.strip
59
+
60
+ if current_version > your_version
61
+ status = c("#{current_version} (consider update)", :red)
62
+ elsif current_version < your_version
63
+ status = c("#{current_version} (ahead, beta)", :green)
64
+ else
65
+ status = c("#{current_version} (up2date)", :green)
66
+ end
67
+ rescue
68
+ status = c("failed (#{$!.message})", :red)
69
+ end
70
+
71
+ print "#{"\b" * 11}#{" " * 11}#{"\b" * 11}" # reset line
72
+ puts status
73
+ else
74
+ puts c("check disabled", :red)
75
+ end
76
+
77
+ # more info
78
+ puts c ""
79
+ puts c " Watchmonkey CLI is brought to you by #{c "bmonkeys.net", :green}"
80
+ puts c " Contribute @ #{c "github.com/2called-chaos/watchmonkey_cli", :cyan}"
81
+ puts c " Eat bananas every day!"
82
+ puts c ""
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,255 @@
1
+ module WatchmonkeyCli
2
+ class Application
3
+ attr_reader :opts, :checkers, :connections, :threads, :queue, :hooks, :processed
4
+ include Helpers
5
+ include Colorize
6
+ include Dispatch
7
+ include Configuration::AppHelper
8
+ include Checker::AppHelper
9
+
10
+ # =========
11
+ # = Setup =
12
+ # =========
13
+ def self.dispatch *a
14
+ new(*a) do |app|
15
+ app.load_config
16
+ app.parse_params
17
+ begin
18
+ app.dispatch
19
+ app.haltpoint
20
+ rescue Interrupt
21
+ app.abort("Interrupted", 1)
22
+ ensure
23
+ app.fire(:wm_shutdown)
24
+ app.debug "#{Thread.list.length} threads remain..."
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize env, argv
30
+ @env, @argv = env, argv
31
+ @connections = {}
32
+ @hooks = {}
33
+ @monitor = Monitor.new
34
+ @threads = []
35
+ @queue = Queue.new
36
+ @processed = 0
37
+ @running = false
38
+ @opts = {
39
+ dump: false, # (internal) if true app will dump itself and exit before running any checks
40
+ dispatch: :index, # (internal) action to dispatch
41
+ check_for_updates: true, # -z flag
42
+ colorize: true, # -m flag
43
+ debug: false, # -d flag
44
+ threads: 10, # -t flag
45
+ loop_forever: false, # (internal) loop forever (app mode)
46
+ loop_wait_empty: 1, # (internal) time to wait in thread if queue is empty
47
+ silent: false, # -s flag
48
+ quiet: false, # -q flag
49
+ }
50
+ init_params
51
+ yield(self)
52
+ end
53
+
54
+ def init_params
55
+ @optparse = OptionParser.new do |opts|
56
+ opts.banner = "Usage: watchmonkey [options]"
57
+
58
+ opts.separator(c "# Application options", :blue)
59
+ opts.on("--generate-config [myconfig]", "Generates a example config in ~/.watchmonkey") {|s| @opts[:dispatch] = :generate_config; @opts[:config_name] = s }
60
+ opts.on("-l", "--log [file]", "Log to file, defaults to ~/.watchmonkey/logs/watchmonkey.log") {|s| @opts[:logfile] = s || logger_filename }
61
+ opts.on("-t", "--threads [NUM]", Integer, "Amount of threads to be used for checking (default: 10)") {|s| @opts[:threads] = s }
62
+ opts.on("-s", "--silent", "Only print errors and infos") { @opts[:silent] = true }
63
+ opts.on("-q", "--quiet", "Only print errors") { @opts[:quiet] = true }
64
+
65
+ opts.separator("\n" << c("# General options", :blue))
66
+ opts.on("-d", "--debug", "Enable debug output") { @opts[:debug] = true }
67
+ opts.on("-m", "--monochrome", "Don't colorize output") { @opts[:colorize] = false }
68
+ opts.on("-h", "--help", "Shows this help") { @opts[:dispatch] = :help }
69
+ opts.on("-v", "--version", "Shows version and other info") { @opts[:dispatch] = :info }
70
+ opts.on("-z", "Do not check for updates on GitHub (with -v/--version)") { @opts[:check_for_updates] = false }
71
+ opts.on("--dump-core", "for developers") { @opts[:dump] = true }
72
+ end
73
+ end
74
+
75
+ def parse_params
76
+ @optparse.parse!(@argv)
77
+ rescue OptionParser::ParseError => e
78
+ abort(e.message)
79
+ dispatch(:help)
80
+ exit 1
81
+ end
82
+
83
+ def running?
84
+ @running
85
+ end
86
+
87
+ def load_config
88
+ return unless File.exist?(wm_cfg_configfile)
89
+ eval File.read(wm_cfg_configfile, encoding: "utf-8"), binding, wm_cfg_configfile
90
+ end
91
+
92
+ def debug msg
93
+ puts c("[DEBUG] #{msg}", :black) if @opts[:debug]
94
+ end
95
+
96
+ def abort msg, exit_code = 1
97
+ puts c("[ABORT] #{msg}", :red)
98
+ exit(exit_code)
99
+ end
100
+
101
+ def error msg
102
+ warn c(msg, :red)
103
+ end
104
+
105
+ def hook *which, &hook_block
106
+ which.each do |w|
107
+ @hooks[w.to_sym] ||= []
108
+ @hooks[w.to_sym] << hook_block
109
+ end
110
+ end
111
+
112
+ def fire which, *args
113
+ return if @disable_event_firing
114
+ sync { debug "[Event] Firing #{which} (#{@hooks[which].try(:length) || 0} handlers) #{args.map(&:class)}" }
115
+ @hooks[which] && @hooks[which].each{|h| h.call(*args) }
116
+ end
117
+
118
+ def fetch_connection type, id, opts = {}, &initializer
119
+ if !@connections[type] || !@connections[type][id]
120
+ @connections[type] ||= {}
121
+ case type
122
+ when :loopback
123
+ @connections[type][id] = LoopbackConnection.new(id, opts, &initializer)
124
+ when :ssh
125
+ @connections[type][id] = SshConnection.new(id, opts, &initializer)
126
+ else
127
+ raise NotImplementedError, "unknown connection type `#{type}'!"
128
+ end
129
+ end
130
+ @connections[type][id]
131
+ end
132
+
133
+ def close_connections!
134
+ @connections.each do |type, clist|
135
+ clist.each{|id, con| con.close! }
136
+ end
137
+ end
138
+
139
+ def sync &block
140
+ @monitor.synchronize(&block)
141
+ end
142
+
143
+ def spawn_threads_and_run!
144
+ if @opts[:threads] > 1
145
+ debug "Spawning #{@opts[:threads]} consumer threads..."
146
+ @opts[:threads].times do
147
+ @threads << Thread.new do
148
+ Thread.current.abort_on_exception = true
149
+ _queueoff
150
+ end
151
+ end
152
+ else
153
+ debug "Running threadless..."
154
+ _queueoff
155
+ end
156
+ end
157
+
158
+ def enqueue checker, *a, &block
159
+ sync do
160
+ cb = block || checker.method(:check!)
161
+ evreg = @disable_event_registration
162
+ fire(:enqueue, checker, a, cb) unless evreg
163
+ @queue << [checker, a, ->(*a) {
164
+ begin
165
+ result = Checker::Result.new(checker, *a)
166
+ checker.debug(result.str_running)
167
+ checker.safe(result.str_safe) { cb.call(result, *a) }
168
+ fire(:result_dump, result, a, checker)
169
+ result.dump!
170
+ ensure
171
+ fire(:dequeue, checker, a) unless evreg
172
+ end
173
+ }]
174
+ end
175
+ end
176
+
177
+ def enqueue_sub checker, which, *args
178
+ sync do
179
+ if sec = @checkers[which.to_s]
180
+ begin
181
+ # ef_was = @disable_event_firing
182
+ er_was = @disable_event_registration
183
+ # @disable_event_firing = true
184
+ @disable_event_registration = true
185
+ sec.enqueue(*args)
186
+ ensure
187
+ # @disable_event_firing = ef_was
188
+ @disable_event_registration = er_was
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ def _queueoff
195
+ while !@queue.empty? || @opts[:loop_forever]
196
+ break if $wm_runtime_exiting
197
+ item = queue.pop(true) rescue false
198
+ if item
199
+ Thread.current[:working] = true
200
+ fire(:wm_work_start, Thread.current)
201
+ sync { @processed += 1 }
202
+ item[2].call(*item[1])
203
+ Thread.current[:working] = false
204
+ fire(:wm_work_end, Thread.current)
205
+ end
206
+ sleep @opts[:loop_wait_empty] if @opts[:loop_forever] && @opts[:loop_wait_empty] && @queue.empty?
207
+ end
208
+ end
209
+
210
+ def wm_cfg_path
211
+ ENV["WM_CFGDIR"].presence || File.expand_path("~/.watchmonkey")
212
+ end
213
+
214
+ def wm_cfg_configfile
215
+ "#{wm_cfg_path}/config.rb"
216
+ end
217
+
218
+ def logger_filename
219
+ "#{wm_cfg_path}/logs/watchmonkey.log"
220
+ end
221
+
222
+ def logger
223
+ sync do
224
+ @logger ||= begin
225
+ FileUtils.mkdir_p(File.dirname(@opts[:logfile]))
226
+ Logger.new(@opts[:logfile], 10, 1024000)
227
+ end
228
+ end
229
+ end
230
+
231
+ def trap_signals
232
+ debug "Trapping INT signal..."
233
+ Signal.trap("INT") do
234
+ $wm_runtime_exiting = true
235
+ puts "Interrupting..."
236
+ end
237
+ end
238
+
239
+ def release_signals
240
+ debug "Releasing INT signal..."
241
+ Signal.trap("INT", "DEFAULT")
242
+ end
243
+
244
+ def haltpoint
245
+ raise Interrupt if $wm_runtime_exiting
246
+ end
247
+
248
+ def dump_and_exit!
249
+ puts " Queue: #{@queue.length}"
250
+ puts " AppOpts: #{@opts}"
251
+ puts "Checkers: #{@checkers.keys.join(",")}"
252
+ exit 9
253
+ end
254
+ end
255
+ end