watchmonkey_cli 1.4

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.
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