module PhusionPassenger::PreloaderSharedHelpers

def accept_and_process_next_client(server_socket)

def accept_and_process_next_client(server_socket)
  original_pid = Process.pid
  client = server_socket.accept
  client.binmode
  begin
    command = client.readline
  rescue EOFError
    return nil
  end
  if command !~ /\n\Z/
    STDERR.puts "Command must end with a newline"
  elsif command == "spawn\n"
    while client.readline != "\n"
      # Do nothing.
    end
    # Improve copy-on-write friendliness.
    GC.start
    pid = fork
    if pid.nil?
      $0 = "#{$0} (forking...)"
      client.puts "OK"
      client.puts Process.pid
      client.flush
      client.sync = true
      return [:forked, client]
    elsif defined?(NativeSupport)
      NativeSupport.detach_process(pid)
    else
      Process.detach(pid)
    end
  else
    STDERR.puts "Unknown command '#{command.inspect}'"
  end
  return nil
ensure
  if client && Process.pid == original_pid
    begin
      client.close
    rescue Errno::EINVAL
      # Work around OS X bug.
      # https://code.google.com/p/phusion-passenger/issues/detail?id=854
    end
  end
end

def init(options)

def init(options)
  if !Kernel.respond_to?(:fork)
    message = "Smart spawning is not available on this Ruby " +
      "implementation because it does not support `Kernel.fork`. "
    if ENV['SERVER_SOFTWARE'].to_s =~ /nginx/i
      message << "Please set `passenger_spawn_method` to `direct`."
    else
      message << "Please set `PassengerSpawnMethod` to `direct`."
    end
    raise(message)
  end
  return options
end

def run_main_loop(options)

def run_main_loop(options)
  $0 = "Passenger AppPreloader: #{options['app_root']}"
  client = nil
  original_pid = Process.pid
  if defined?(NativeSupport)
    unix_path_max = NativeSupport::UNIX_PATH_MAX
  else
    unix_path_max = options.fetch('UNIX_PATH_MAX', 100).to_i
  end
  if options['socket_dir']
    socket_dir = options['socket_dir']
    socket_prefix = "preloader"
  else
    socket_dir = Dir.tmpdir
    socket_prefix = "PsgPreloader"
  end
  socket_filename = nil
  server = nil
  Utils.retry_at_most(128, Errno::EADDRINUSE) do
    socket_filename = "#{socket_dir}/#{socket_prefix}.#{rand(0xFFFFFFFF).to_s(36)}"
    socket_filename = socket_filename.slice(0, unix_path_max - 10)
    server = UNIXServer.new(socket_filename)
  end
  server.close_on_exec!
  File.chmod(0600, socket_filename)
  # Update the dump information just before telling the preloader that we're
  # ready because the HelperAgent will read and memorize this information.
  LoaderSharedHelpers.dump_all_information(options)
  puts "!> Ready"
  puts "!> socket: unix:#{socket_filename}"
  puts "!> "
  while true
    # We call ::select just in case someone overwrites the global select()
    # function by including ActionView::Helpers in the wrong place.
    # https://code.google.com/p/phusion-passenger/issues/detail?id=915
    ios = Kernel.select([server, STDIN])[0]
    if ios.include?(server)
      result, client = accept_and_process_next_client(server)
      if result == :forked
        STDIN.reopen(client)
        STDOUT.reopen(client)
        STDOUT.sync = true
        client.close
        return :forked
      end
    end
    if ios.include?(STDIN)
      if STDIN.tty?
        begin
          # Prevent bash from exiting when we press Ctrl-D.
          STDIN.read_nonblock(1)
        rescue Errno::EAGAIN
          # Do nothing.
        end
      end
      break
    end
  end
  return nil
ensure
  server.close if server
  if original_pid == Process.pid
    File.unlink(socket_filename) rescue nil
  end
end