diff --git a/Gemfile b/Gemfile index b4d2b5e..c8cbc71 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,3 @@ source "http://rubygems.org" # Specify your gem's dependencies in forever.gemspec gemspec - -gem 'guard' -gem 'guard-rspec' -gem 'growl' \ No newline at end of file diff --git a/Guardfile b/Guardfile deleted file mode 100644 index 7d10dc5..0000000 --- a/Guardfile +++ /dev/null @@ -1,5 +0,0 @@ -guard 'rspec', :cli => '--color' do - watch(%r{^spec/.+_spec\.rb$}) - watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } - watch('spec/spec_helper.rb') { "spec" } -end \ No newline at end of file diff --git a/Rakefile b/Rakefile index 7b793d8..4261fc8 100644 --- a/Rakefile +++ b/Rakefile @@ -10,7 +10,7 @@ end desc "Bump version on github" task :bump do - puts "\e[31mNothing to commit (working directory clean)\e[0m" and return unless `git status -s`.chomp! + puts "\e[31mNothing to commit (working directory clean)\e[0m" and return unless `git status -s`.strip == "" version = Bundler.load_gemspec(Dir[File.expand_path('../*.gemspec', __FILE__)].first).version sh "git add .; git commit -a -m \"Bump to version #{version}\"" end @@ -24,4 +24,4 @@ RSpec::Core::RakeTask.new("spec") do |t| t.rspec_opts = %w(-fs --color --fail-fast) end -task :default => :spec \ No newline at end of file +task :default => :spec diff --git a/bin/foreverb b/bin/foreverb index 094d174..f1f1eeb 100755 --- a/bin/foreverb +++ b/bin/foreverb @@ -50,6 +50,23 @@ class CLI < Thor end end + desc "kill [DAEMON] [--all] [--yes]", "Kill one or more matching daemons" + method_option :all, :type => :boolean, :aliases => "-a", :desc => "All matching daemons" + method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to kill daemon" + def kill(daemon=nil) + find(daemon, :multiple => options.all).each do |conf| + if options.yes || yes?("Do you want really kill \e[1m#{conf[:file]}\e[0m?") + say_status "KILLING", conf[:file] + begin + pid = File.read(conf[:pid]).to_i + Process.kill(:INT, pid) + rescue Exception => e + say_status "ERROR", e.message, :red + end + end + end + end + desc "start [DAEMON] [--all] [--yes]", "Start one or more matching daemons" method_option :all, :type => :boolean, :aliases => "-a", :desc => "All matching daemons" method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to start the daemon" @@ -122,4 +139,4 @@ class CLI < Thor end ARGV << "-h" if ARGV.empty? -CLI.start(ARGV) \ No newline at end of file +CLI.start(ARGV) diff --git a/examples/sample b/examples/sample index c42295e..bee4a3b 100755 --- a/examples/sample +++ b/examples/sample @@ -6,6 +6,10 @@ require 'forever' Forever.run do dir File.expand_path('../', __FILE__) # Default is ../../__FILE__ + every 5.seconds do + puts 'every 5 seconds' + end + on_ready do puts "All jobs will will wait me for 1 second"; sleep 1 end @@ -50,4 +54,4 @@ Forever.run do on_exit do puts "Bye bye" end -end \ No newline at end of file +end diff --git a/lib/forever/base.rb b/lib/forever/base.rb index 507584f..6f2e7e1 100644 --- a/lib/forever/base.rb +++ b/lib/forever/base.rb @@ -16,20 +16,37 @@ def initialize(options={}, &block) write_config! - if ARGV.any? { |arg| arg == "config" } - print config.to_yaml - return + case ARGV[0] + when 'config' + print config.to_yaml + exit + when 'start', 'restart', 'up', nil + stop + when 'stop' + stop + exit + when 'kill' + stop! + exit + when 'help', '-h' + print <<-RUBY.gsub(/ {10}/,'') % File.basename(file) + Usage: \e[1m./%s\e[0m [start|stop|kill|restart|config] + + Commands: + + start stop (if present) the daemon and perform a start + stop stop the daemon if a during when it is idle + restart same as start + kill force stop by sending a KILL signal to the process + config show the current daemons config + + RUBY + exit end - return if ARGV.any? { |arg| arg == "up" } - - stop! - - return if ARGV.any? { |arg| arg == "stop" } - fork do $0 = "Forever: #{$0}" - print "=> Process demonized with pid #{Process.pid} with Forever v.#{Forever::VERSION}\n" + print "=> Process demonized with pid \e[1m#{Process.pid}\e[0m with Forever v.#{Forever::VERSION}\n" %w(INT TERM KILL).each { |signal| trap(signal) { stop! } } trap(:HUP) do @@ -46,12 +63,25 @@ def initialize(options={}, &block) threads = [] safe_call(on_ready) if on_ready + started_at = Time.now + jobs.each do |job| threads << Thread.new do - loop { job.time?(Time.now) ? safe_call(job) : sleep(1) } + loop do + break if File.exist?(stop_txt) && File.mtime(stop_txt) > started_at + job.time?(Time.now) ? safe_call(job) : sleep(1) + end end end + + # Launch our workers threads.map(&:join) + + # If we are here it means we are exiting so we can remove the pid and pending stop.txt + FileUtils.rm_f(pid) + FileUtils.rm_f(stop_txt) + + on_exit.call if on_exit end self @@ -95,19 +125,30 @@ def pid(value=nil) # Search if there is a running process and stop it # def stop! - if exists?(pid) - _pid = File.read(pid).to_i - print "=> Found pid #{_pid}...\n" + FileUtils.rm_f(stop_txt) + if running? + pid_was = File.read(pid).to_i FileUtils.rm_f(pid) - begin - print "=> Killing process #{_pid}...\n" - on_exit.call if on_exit - Process.kill(:KILL, _pid) - rescue Errno::ESRCH => e - puts "=> #{e.message}" - end + print "=> Killing process \e[1m%d\e[0m...\n" % pid_was + on_exit.call if on_exit + Process.kill(:KILL, pid_was) else - print "=> Pid not found, process seems don't exist!\n" + print "=> Process with \e[1mnot found\e[0m" + end + end + + ## + # Perform a soft stop + # + def stop + if running? + print '=> Waiting the daemon\'s death ' + FileUtils.touch(stop_txt) + while running?(true) + print '.'; $stdout.flush + sleep 1 + end + print " \e[1mDONE\e[0m\n" end end @@ -132,6 +173,27 @@ def on_ready(&block) block_given? ? @_on_ready = block : @_on_ready end + ## + # Returns true if the pid exist and the process is running + # + def running?(silent=false) + if exists?(pid) + current = File.read(pid).to_i + print "=> Found pid \e[1m%d\e[0m...\n" % current unless silent + else + print "=> Pid \e[1mnot found\e[0m, process seems don't exist!\n" unless silent + return false + end + + is_running = begin + Process.kill(0, current) + rescue Errno::ESRCH + false + end + + is_running + end + def to_s "#" end @@ -161,5 +223,9 @@ def safe_call(block) on_error[e] if on_error end end + + def stop_txt + @_stop_txt ||= File.join(dir, 'stop.txt') + end end # Base -end # Forever \ No newline at end of file +end # Forever diff --git a/lib/forever/version.rb b/lib/forever/version.rb index 1ce0713..7658639 100644 --- a/lib/forever/version.rb +++ b/lib/forever/version.rb @@ -1,3 +1,3 @@ module Forever - VERSION = "0.2.4" unless defined?(Forever::VERSION) + VERSION = "0.2.5" unless defined?(Forever::VERSION) end diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index b0274e1..9dc314e 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -22,4 +22,13 @@ result.should_not match(/ERROR/) cli('list').should match(/NOT RUNNING/) end -end \ No newline at end of file + + it "should kill daemons" do + run_example + cli('list').should match(/RUNNING/) + result = cli('kill -a -y') + result.should match(/KILLING/) + result.should_not match(/ERROR/) + cli('list').should match(/NOT RUNNING/) + end +end diff --git a/spec/foreverb_spec.rb b/spec/foreverb_spec.rb index fb0ff12..62ac4c9 100644 --- a/spec/foreverb_spec.rb +++ b/spec/foreverb_spec.rb @@ -3,6 +3,7 @@ describe Forever do before :each do + $stdout = StringIO.new ARGV << 'up' end @@ -41,7 +42,7 @@ sleep 0.1 while !File.exist?(@forever.pid) pid = File.read(@forever.pid).to_i Process.waitpid(pid) - $stdout.string.should match(/pid not found/i) + $stdout.string.should match(/not found/i) $stdout = stdout_was end -end \ No newline at end of file +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 198b427..f0a6333 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -45,4 +45,4 @@ def cli(task) end ARGV.clear end -end \ No newline at end of file +end