class FireAndForget::Command::Fire

Attributes

niceness[R]
task_gid[R]
task_uid[R]

Public Class Methods

new(task, params={}) click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 8
def initialize(task, params={})
  super
  @task_uid = Process.euid
  @task_gid = Process.egid
end

Public Instance Methods

binary() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 18
def binary
  @task.binary
end
binary_file() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 22
def binary_file
  @task.binary.split(" ").first
end
cmd() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 26
def cmd
  %(#{binary} #{FireAndForget.to_arguments(@params)})
end
debug() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 71
def debug
  "Fire :#{@task.name}: #{cmd}\n"
end
env() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 44
def env
  @task.env.merge({
    FireAndForget::ENV_SOCKET => FireAndForget.socket,
    FireAndForget::ENV_TASK_NAME => @task.name.to_s
  })
end
exists?() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 39
def exists?
  raise FileNotFoundError, "'#{binary_file}'" unless File.exists?(binary_file)
  true
end
permitted?() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 34
def permitted?
  raise PermissionsError, "'#{binary_file}' does not belong to user '#{ENV["USER"]}'" unless File.stat(binary_file).uid == task_uid
  true
end
run() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 51
def run
  if valid?
    pid = fork do
      # set up the environment so that the task can access the F&F server
      env.each { | k, v | ENV[k] = v }
      # TODO: figure out how to pass a logfile path to this
      daemonize
      Process.setpriority(Process::PRIO_PROCESS, 0, niceness) if niceness > 0
      # change to the UID of the originating thread if necessary
      Process::UID.change_privilege(task_uid) unless Process.euid == task_uid
      File.umask(0022)
      exec(cmd)
    end
    Process.detach(pid) if pid
    # don't return the PID as it's actually wrong (the daemonize call forks again so our original
    # PID is at least 1 out)
    "OK"
  end
end
valid?() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 30
def valid?
  exists? && permitted?
end

Private Instance Methods

daemonize(logfile_name = nil, app_name = nil) click to toggle source

The following adapted from Daemons.daemonize

# File lib/fire_and_forget/command/fire.rb, line 78
def daemonize(logfile_name = nil, app_name = nil)
  srand # Split rand streams between spawning and daemonized process
    safefork and exit # Fork and exit from the parent

  # Detach from the controlling terminal
  unless sess_id = Process.setsid
    raise RuntimeException.new('cannot detach from controlling terminal')
  end

  # Prevent the possibility of acquiring a controlling terminal
  trap 'SIGHUP', 'IGNORE'
  exit if pid = safefork

  $0 = app_name if app_name

  Dir.chdir "/"   # Release old working directory
  File.umask 0000 # Ensure sensible umask

  # Make sure all file descriptors are closed
  ObjectSpace.each_object(IO) do |io|
    unless [STDIN, STDOUT, STDERR].include?(io)
      begin
        unless io.closed?
          io.close
        end
      rescue ::Exception
      end
    end
  end

  redirect_io(logfile_name)

  return sess_id
end
redirect_io(logfile_name) click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 113
def redirect_io(logfile_name)
  begin; STDIN.reopen "/dev/null"; rescue ::Exception; end

  if logfile_name
    begin
      STDOUT.reopen logfile_name, "a"
      File.chmod(0644, logfile_name)
      STDOUT.sync = true
    rescue ::Exception
      begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
    end
  else
    begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
  end

  begin; STDERR.reopen STDOUT; rescue ::Exception; end
  STDERR.sync = true
end
safefork() click to toggle source
# File lib/fire_and_forget/command/fire.rb, line 132
def safefork
  tryagain = true

  while tryagain
    tryagain = false
    begin
      if pid = fork
        return pid
      end
    rescue Errno::EWOULDBLOCK
      sleep 5
      tryagain = true
    end
  end
end