]> git.cworth.org Git - sup/commitdiff
multiple concurrent process detection and resolution
authorwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Mon, 4 Jun 2007 01:56:26 +0000 (01:56 +0000)
committerwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Mon, 4 Jun 2007 01:56:26 +0000 (01:56 +0000)
git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@430 5c8cc53c-5e98-4d25-b20a-d8db53a31250

Manifest.txt
Rakefile
bin/sup
lib/sup.rb
lib/sup/suicide.rb [new file with mode: 0644]

index 971f0ca4eff3acee3e44ccec4d027aea83272c8a..8d90c399bee698055be6a67417e9f27824c6853d 100644 (file)
@@ -57,6 +57,7 @@ lib/sup/person.rb
 lib/sup/poll.rb
 lib/sup/sent.rb
 lib/sup/source.rb
+lib/sup/suicide.rb
 lib/sup/tagger.rb
 lib/sup/textfield.rb
 lib/sup/thread.rb
index 176031d4c1f61d53dae7db679f4edd4b9415a19e..cc4a12a1b844a490ac8bc14f09cb85cdb91edfdd 100644 (file)
--- a/Rakefile
+++ b/Rakefile
@@ -16,7 +16,7 @@ Hoe.new('sup', Redwood::VERSION) do |p|
   p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[2].gsub(/^\s+/, "")
   p.changes = p.paragraphs_of('History.txt', 0..0).join("\n\n")
   p.email = "wmorgan-sup@masanjin.net"
-  p.extra_deps = [['ferret', '>= 0.10.13'], ['ncurses', '>= 0.9.1'], ['rmail', '>= 0.17'], 'highline', 'net-ssh', ['trollop', '>= 1.5']]
+  p.extra_deps = [['ferret', '>= 0.10.13'], ['ncurses', '>= 0.9.1'], ['rmail', '>= 0.17'], 'highline', 'net-ssh', ['trollop', '>= 1.5'], 'lockfile']
 end
 
 rule 'ss?.png' => 'ss?-small.png' do |t|
diff --git a/bin/sup b/bin/sup
index 273ef01914e1d73c19d3d566c6fe9c5cfa73907a..ab835e7ba4edcdec548cf43cef117ddafb6fe493 100644 (file)
--- a/bin/sup
+++ b/bin/sup
@@ -2,6 +2,7 @@
 
 require 'rubygems'
 require 'ncurses'
+require 'fileutils'
 require 'trollop'
 require "sup"
 
@@ -54,6 +55,30 @@ def stop_cursing
 end
 module_function :start_cursing, :stop_cursing
 
+begin
+  Redwood::lock
+rescue LockError => e
+  require 'highline'
+  h = HighLine.new
+  h.say <<EOS
+Error: sup is already running! User #{e.user} on host #{e.host} was running sup
+with pid #{e.pid} as of #{e.time}.
+EOS
+
+  case h.ask("Should I try and kill that process? ")
+  when /^\s*y\s*$/i
+    h.say "Ok, suggesting sepuku..."
+    FileUtils.touch Redwood::SUICIDE_FN
+    sleep SuicideManager::DELAY * 2
+    FileUtils.rm_f Redwood::SUICIDE_FN
+    h.say "Let's try that again."
+    retry
+  else
+    h.say "Ok, see you later."
+    exit
+  end
+end
+
 Redwood::start
 Index.new.load
 
@@ -140,7 +165,10 @@ begin
 
   imode.load_threads :num => ibuf.content_height, :when_done => lambda { reporting_thread { sleep 1; PollManager.poll } }
 
-  PollManager.start_thread unless $opts[:no_threads]
+  unless $opts[:no_threads]
+    PollManager.start_thread
+    SuicideManager.start_thread
+  end
 
   until $exception
     bm.draw_screen
@@ -223,14 +251,20 @@ rescue Exception => e
 ensure
   Redwood::finish
   stop_cursing
-  if $exception
-    Redwood::log "oh crap, an exception"
-  else
+
+  case $exception
+  when SuicideException
+    Redwood::log "I've been asked to commit sepuku. I obey!"
+    exit
+  when nil
     Redwood::log "good night, sweet prince!"
+    Index.save
+  else
+    Redwood::log "oh crap, an exception"
   end
-end
 
-Index.save unless $exception # TODO: think about this
+  Redwood::unlock
+end
 
 if $exception 
   $stderr.puts <<EOS
index 5311a4404126f8b85285a590ef761e3ae1db2eb9..ff11dcc2ac8065af37c856a6c101f299a81f4038 100644 (file)
@@ -3,6 +3,29 @@ require 'yaml'
 require 'zlib'
 require 'thread'
 require 'fileutils'
+require 'lockfile'
+
+## time for some monkeypatching!
+class Lockfile
+  def gen_lock_id
+    Hash[
+         'host' => "#{ Socket.gethostname }",
+         'pid' => "#{ Process.pid }",
+         'ppid' => "#{ Process.ppid }",
+         'time' => timestamp,
+         'user' => ENV["USER"]
+        ]
+  end
+
+  def dump_lock_id lock_id = @lock_id
+      "host: %s\npid: %s\nppid: %s\ntime: %s\nuser: %s\n" %
+        lock_id.values_at('host','pid','ppid','time','user')
+    end
+
+  def lockinfo_on_disk
+    load_lock_id IO.read(path)
+  end
+end
 
 class Object
   ## this is for debugging purposes because i keep calling #id on the
@@ -12,6 +35,15 @@ class Object
   end
 end
 
+class LockError < StandardError
+  def initialize h
+    super ""
+    @h = h
+  end
+
+  def method_missing m; @h[m.to_s] end
+end
+
 class Module
   def yaml_properties *props
     props = props.map { |p| p.to_s }
@@ -41,6 +73,8 @@ module Redwood
   CONTACT_FN = File.join(BASE_DIR, "contacts.txt")
   DRAFT_DIR  = File.join(BASE_DIR, "drafts")
   SENT_FN    = File.join(BASE_DIR, "sent.mbox")
+  LOCK_FN    = File.join(BASE_DIR, "lock")
+  SUICIDE_FN = File.join(BASE_DIR, "please-kill-yourself")
 
   YAML_DOMAIN = "masanjin.net"
   YAML_DATE = "2006-10-01"
@@ -58,7 +92,7 @@ module Redwood
           File.open("sup-exception-log.txt", "w") do |f|
             f.puts "--- #{e.class.name} at #{Time.now}"
             f.puts e.message, e.backtrace
-          end
+          end unless e.is_a? SuicideException
           $exception ||= e
           raise
         end
@@ -95,6 +129,7 @@ module Redwood
     Redwood::DraftManager.new Redwood::DRAFT_DIR
     Redwood::UpdateManager.new
     Redwood::PollManager.new
+    Redwood::SuicideManager.new Redwood::SUICIDE_FN
   end
 
   def finish
@@ -104,6 +139,23 @@ module Redwood
     Redwood::BufferManager.deinstantiate!
   end
 
+  def lock
+    FileUtils.rm_f SUICIDE_FN
+
+    Redwood::log "locking #{LOCK_FN}..."
+    $lock = Lockfile.new LOCK_FN, :retries => 0
+      begin
+        $lock.lock
+      rescue Lockfile::MaxTriesLockError
+        raise LockError, $lock.lockinfo_on_disk
+      end
+  end
+
+  def unlock
+    Redwood::log "unlocking #{LOCK_FN}..."
+    $lock.unlock if $lock
+  end
+
   ## not really a good place for this, so I'll just dump it here.
   def report_broken_sources opts={}
     return unless BufferManager.instantiated?
@@ -146,7 +198,8 @@ EOM
     end
   end
 
-  module_function :save_yaml_obj, :load_yaml_obj, :start, :finish, :report_broken_sources
+  module_function :save_yaml_obj, :load_yaml_obj, :start, :finish,
+                  :lock, :unlock, :report_broken_sources
 end
 
 ## set up default configuration file
@@ -186,6 +239,7 @@ end
 
 require "sup/util"
 require "sup/update"
+require "sup/suicide"
 require "sup/message"
 require "sup/source"
 require "sup/mbox"
diff --git a/lib/sup/suicide.rb b/lib/sup/suicide.rb
new file mode 100644 (file)
index 0000000..6c3141e
--- /dev/null
@@ -0,0 +1,29 @@
+require 'fileutils'
+module Redwood
+
+class SuicideException < StandardError; end
+
+class SuicideManager
+  include Singleton
+
+  DELAY = 5
+
+  def initialize fn
+    @fn = fn
+    self.class.i_am_the_instance self
+  end
+
+  def start_thread
+    Redwood::reporting_thread do
+      while true
+        sleep DELAY
+        if File.exists? @fn
+          FileUtils.rm_rf @fn
+          raise SuicideException
+        end
+      end
+    end
+  end
+end
+
+end