# class Foreman::Engine

### Constants

HANDLED_SIGNALS

  The signals that the engine cares about.

### Attributes

env[R]
options[R]
processes[R]

### Public Class Methods

new(options={}) click to toggle source

Create an `Engine` for running processes

@param [Hash] options

@option options [String] :formation (all=1) The process formation to use @option options [Fixnum] :port (5000) The base port to assign to processes @option options [String] :root (Dir.pwd) The root directory from which to run processes

    # File lib/foreman/engine.rb, line 28
    def initialize(options={})
      @options = options.dup

      @options[:formation] ||= (options[:concurrency] || "all=1")
      @options[:timeout] ||= 5

      @env       = {}
      @mutex     = Mutex.new
      @names     = {}
      @processes = []
      @running   = {}
      @readers   = {}

      # Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html)
      reader, writer       = create_pipe
      reader.close_on_exec = true if reader.respond_to?(:close_on_exec)
      writer.close_on_exec = true if writer.respond_to?(:close_on_exec)
      @selfpipe            = { :reader => reader, :writer => writer }

      # Set up a global signal queue
      # http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html
      Thread.main[:signal_queue] = []
    end

### Public Instance Methods

base_port() click to toggle source

Get the base port for this foreman instance

@returns [Fixnum] port The base port

    # File lib/foreman/engine.rb, line 274
    def base_port
      (options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
    end

clear() click to toggle source

Clear the processes registered to this `Engine`

    # File lib/foreman/engine.rb, line 150
    def clear
      @names     = {}
      @processes = []
    end

each_process() { |name, process(name)| ... } click to toggle source

Yield each `Process` in order

    # File lib/foreman/engine.rb, line 241
    def each_process
      process_names.each do |name|
        yield name, process(name)
      end
    end

environment() click to toggle source

deprecated

    # File lib/foreman/engine.rb, line 279
    def environment
      env
    end

formation() click to toggle source

Get the process formation

@returns [Fixnum] The formation count for the specified process

    # File lib/foreman/engine.rb, line 217
    def formation
      @formation ||= parse_formation(options[:formation])
    end

handle_hangup() click to toggle source

Handle a HUP signal

    # File lib/foreman/engine.rb, line 127
    def handle_hangup
      puts "SIGHUP received"
      terminate_gracefully
    end

handle_interrupt() click to toggle source

Handle an INT signal

    # File lib/foreman/engine.rb, line 120
    def handle_interrupt
      puts "SIGINT received"
      terminate_gracefully
    end

handle_signal(sig) click to toggle source

Invoke the real handler for signal `sig`. This shouldn't be called directly by signal handlers, as it might invoke code which isn't re-entrant.

@param [Symbol] sig the name of the signal to be handled

    # File lib/foreman/engine.rb, line 98
    def handle_signal(sig)
      case sig
      when :TERM
        handle_term_signal
      when :INT
        handle_interrupt
      when :HUP
        handle_hangup
      else
        system "unhandled signal #{sig}"
      end
    end

handle_term_signal() click to toggle source

Handle a TERM signal

    # File lib/foreman/engine.rb, line 113
    def handle_term_signal
      puts "SIGTERM received"
      terminate_gracefully
    end

kill_children(signal="SIGTERM") click to toggle source

Send a signal to all processes started by this `Engine`

@param [String] signal The signal to send to each process

    # File lib/foreman/engine.rb, line 181
    def kill_children(signal="SIGTERM")
      if Foreman.windows?
        @running.each do |pid, (process, index)|
          system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
          begin
            Process.kill(signal, pid)
          rescue Errno::ESRCH, Errno::EPERM
          end
        end
      else
        begin
          Process.kill signal, *@running.keys unless @running.empty?
        rescue Errno::ESRCH, Errno::EPERM
        end
      end
    end

killall(signal="SIGTERM") click to toggle source

Send a signal to the whole process group.

@param [String] signal The signal to send

    # File lib/foreman/engine.rb, line 202
    def killall(signal="SIGTERM")
      if Foreman.windows?
        kill_children(signal)
      else
        begin
          Process.kill "-#{signal}", Process.pid
        rescue Errno::ESRCH, Errno::EPERM
        end
      end
    end

load_env(filename) click to toggle source

Load a .env file into the `env` for this `Engine`

@param [String] filename A .env file to load into the environment

    # File lib/foreman/engine.rb, line 171
    def load_env(filename)
      Foreman::Env.new(filename).entries do |name, value|
        @env[name] = value
      end
    end

load_procfile(filename) click to toggle source

Register processes by reading a Procfile

@param [String] filename A Procfile from which to read processes to register

    # File lib/foreman/engine.rb, line 159
    def load_procfile(filename)
      options[:root] ||= File.dirname(filename)
      Foreman::Procfile.new(filename).entries do |name, command|
        register name, command, :cwd => options[:root]
      end
      self
    end

notice_signal() click to toggle source

Wake the main thread up via the selfpipe when there's a signal

    # File lib/foreman/engine.rb, line 84
    def notice_signal
      @selfpipe[:writer].write_nonblock( '.' )
    rescue Errno::EAGAIN
      # Ignore writes that would block
    rescue Errno::EINT
      # Retry if another signal arrived while writing
      retry
    end

port_for(process, instance, base=nil) click to toggle source

Get the port for a given process and offset

@param [Foreman::Process] process A `Process` associated with this engine @param [Fixnum] instance The instance of the process

@returns [Fixnum] port The port to use for this instance of this process

    # File lib/foreman/engine.rb, line 262
    def port_for(process, instance, base=nil)
      if base
        base + (@processes.index(process.process) * 100) + (instance - 1)
      else
        base_port + (@processes.index(process) * 100) + (instance - 1)
      end
    end

process(name) click to toggle source

Get the `Process` for a specifid name

@param [String] name The process name

@returns [Foreman::Process] The `Process` for the specified name

    # File lib/foreman/engine.rb, line 235
    def process(name)
      @names.invert[name]
    end

process_names() click to toggle source

List the available process names

@returns [Array] A list of process names

    # File lib/foreman/engine.rb, line 225
    def process_names
      @processes.map { |p| @names[p] }
    end

register(name, command, options={}) click to toggle source

Register a process to be run by this `Engine`

@param [String] name A name for this process @param [String] command The command to run @param [Hash] options

@option options [Hash] :env A custom environment for this process

    # File lib/foreman/engine.rb, line 140
    def register(name, command, options={})
      options[:env] ||= env
      options[:cwd] ||= File.dirname(command.split(" ").first)
      process = Foreman::Process.new(command, options)
      @names[process] = name
      @processes << process
    end

register_signal_handlers() click to toggle source

Set up deferred signal handlers

    # File lib/foreman/engine.rb, line 66
    def register_signal_handlers
      HANDLED_SIGNALS.each do |sig|
        if ::Signal.list.include? sig.to_s
          trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal }
        end
      end
    end

restore_default_signal_handlers() click to toggle source

Unregister deferred signal handlers

    # File lib/foreman/engine.rb, line 76
    def restore_default_signal_handlers
      HANDLED_SIGNALS.each do |sig|
        trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s
      end
    end

root() click to toggle source

Get the root directory for this `Engine`

@returns [String] The root directory

    # File lib/foreman/engine.rb, line 251
    def root
      File.expand_path(options[:root] || Dir.pwd)
    end

start() click to toggle source

Start the processes registered to this `Engine`

    # File lib/foreman/engine.rb, line 54
    def start
      register_signal_handlers
      startup
      spawn_processes
      watch_for_output
      sleep 0.1
      watch_for_termination { terminate_gracefully }
      shutdown
    end