double-z
3/18/2014 - 11:08 PM

gistfile1.txt

container = LXC::Container.new('foo')
listener = ChefMetalLXC::LXCMessagePasser.attach(container) do |host, number|
  puts "received #{number} INSIDE!"
  if number > 10
    host.stop
  else
    host.send_message(number+1)
  end
end
listener.send_message(1)
listener.listen do |container, number|
  puts "received #{number} OUTSIDE!"
  container.send_message(number+1) unless number > 10
end
module ChefMetalLXC
  #
  # This class is intended to be created before ct.attach.  You can listen
  # to it for messages from The Other Side.
  #
  # Shorthand Usage:
  # listener = LXCMessagePasser.attach(container) do |host, number|
  #   "received #{number} INSIDE!"
  #   if number > 10
  #     host.stop
  #   else
  #     host.send_message(number+1)
  #   end
  # end
  # listener.listen do |container, number|
  #   "received #{number} OUTSIDE!"
  #   container.send_message(number+1) unless number > 10
  # end
  #
  # Usage:
  # listener = LXCMessagePasser.new
  # ct.attach do
  #   listener.listen do |host, number|
  #     "received #{number} INSIDE!"
  #     if number > 10
  #       host.stop
  #     else
  #       host.send(number+1)
  #     end
  #   end
  # end
  # listener.listen do |container, number|
  #   "received #{number} OUTSIDE!"
  #   container.send(number+1)
  # end
  #
  class LXCMessagePasser
    def self.attach(container, &block)
      listener = LXCMessagePasser.new
      container.attach do
        listener.listen(&block)
      end
      listener
    end

    def initialize
      @from_container, @to_host = IO.pipe
      @from_host, @to_container = IO.pipe
      @host_pid = Process.pid
    end

    def listen(&block)
      while true
        args = self.next
        if args.size == 1 && args[0].is_a?(Stop)
          return
        end
        begin
          block.call(self, *args)
        rescue
          raise
        end
      end
    end

    def send_message(*args)
      if Process.pid == @host_pid
        Marshal.dump(args, @to_container)
      else
        Marshal.dump(args, @to_host)
      end
    end

    def next
      if Process.pid == @host_pid
        args = Marshal.load(@from_container)
      else
        args = Marshal.load(@from_host)
      end
      args
    end

    def stop
      send_message(Stop.new)
      @from_container.close
      @to_host.close
      @from_host.close
      @to_container.close
    end

    private

    class Stop
    end
  end
end
root@test:/mnt/host_src/chef-metal-lxc# ruby -I lib lxccheck.rb
received 1 INSIDE!
received 2 OUTSIDE!
received 3 INSIDE!
received 4 OUTSIDE!
received 5 INSIDE!
received 6 OUTSIDE!
received 7 INSIDE!
received 8 OUTSIDE!
received 9 INSIDE!
received 10 OUTSIDE!
received 11 INSIDE!