#!/usr/bin/env ruby # Try to load external libs, helpers and constants begin require "thin" require "optparse" require "socket" # require "timeout" require "settingslogic" rescue LoadError raise "EXITING: some of basic libs were not found: thin, optparse, socket, settingslogic" end [:constants, :helpers].each do |lib| require_relative "../lib/rest-ftp-daemon/#{lib}" end puts # Detect options from ARGV options = {} parser = OptionParser.new do |opts| opts.banner = "Usage: #{File.basename $PROGRAM_NAME} [options] start|stop" opts.on("-c", "--config CONFIGFILE") { |config| APP_CONF = config } opts.on("-e", "--environment ENV") { |env| APP_ENV = env } opts.on("", "--dev") { APP_ENV = "development" } opts.on("-p", "--port PORT", "use PORT") { |port| options["port"] = port.to_i } opts.on("-d", "--daemonize", "Run daemonized in the background") { options["daemonize"] = true } opts.on("-f", "--foreground", "Run in the foreground") { options["daemonize"] = false } opts.on("-P", "--pid FILE", "File to store PID") { |file| options["pidfile"] = file } opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| options["user"] = user } opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| options["group"] = group } opts.separator "" opts.on_tail("-h", "--help", "Show this message") do puts opts puts TAIL_MESSAGE unless File.exist?(DEFAULT_CONFIG_PATH) exit end opts.on_tail("-v", "--version", "Show version (#{APP_VER})") { puts(APP_VER) && exit } end # Parse options and check compliance begin parser.order!(ARGV) command = ARGV.shift rescue OptionParser::InvalidOption => e abort "EXITING: option parser: #{e.message}" else abort parser.to_s unless ["start", "stop"].include? command end # Load config, and merge options from ARGV into settings # FIXME: file configuration detection could reside in settings.rb APP_CONF ||= DEFAULT_CONFIG_PATH APP_ENV ||= ENV_PRODUCTION abort "EXITING: cannot read configuration file: #{APP_CONF}" unless File.exist? APP_CONF begin # Import settings # require File.expand_path("#{app_root}/lib/rest-ftp-daemon/settings") require_relative "../lib/rest-ftp-daemon/settings" # Set defaults Settings.init_defaults # Overwrite with commandline options Settings.merge!(options) # Init NewRelic Settings.init_newrelic rescue Psych::SyntaxError => e abort "EXITING: config file syntax error: #{e.message}" rescue StandardError => e abort "EXITING: unknow error loading settings #{e.inspect}" end # Validate network configuration if command == "start" if !Settings.port abort "ABORTING: Network port is missing" elsif RestFtpDaemon::Helpers.local_port_used? Settings.port abort "ABORTING: Network port #{Settings.port} is already in use" end end # ARGV: build final argv = [] argv << ["-e", Settings.namespace] argv << ["-p", Settings.port.to_s] if Settings.port argv << ["--pid", Settings.pidfile] argv << ["--tag", "'#{APP_NAME}/#{Settings.namespace}'"] argv << ["--daemonize"] if [1, true].include? Settings.daemonize # ARGV: logging, user and group, command if (logfile = Settings.at :logs, :thin) argv << ["--log", logfile] if logfile end if Settings.user && Settings.group argv << ["--user", Settings.user] argv << ["--group", Settings.group] end argv << command unless command.nil? # Display final configuration puts "--- #{APP_NAME} #{APP_VER}" puts "Config file \t #{APP_CONF}" puts "PID file \t #{Settings.pidfile}" puts "Daemonize \t #{Settings.daemonize ? "YES" : "no (PID: #{Process.pid})"}" puts "Host \t #{Settings.host}" puts "Namespace \t #{Settings.namespace}" puts "Network port \t #{Settings.port}" puts "User:group \t #{Settings.user}:#{Settings.group}" if Settings.user || Settings.group puts "Newrelic \t #{Settings.newrelic_enabled? ? Settings.at(:newrelic, :licence) : "no"}" puts "Started at \t #{APP_STARTED}" # puts Settings.dump puts puts "--- Thin ARGV" puts argv.flatten.join(" ") puts # Start Thin with this rackup configuration, changing to app_root first begin app_root = File.expand_path(File.dirname(__FILE__) + "/../") Dir.chdir app_root Thin::Runner.new(argv.flatten).run! rescue RuntimeError => e puts "FAILED: RuntimeError: #{e.message}" rescue Thin::PidFileExist puts "FAILED: daemon was already running (Thin::PidFileExist)" rescue Thin::PidFileNotFound puts "FAILED: daemon was not running (Thin::PidFileNotFound)" rescue Errno::ENOENT => e puts "FAILED: daemon cannot access files: #{e.message}" rescue SystemExit # Leave some time for the daemon to launch pidfile = Settings.pidfile print "Waiting for pidfile" until File.file?(pidfile) do print "." sleep 0.25 end puts # Check that this process is running pid = File.read(pidfile).to_i begin Process.kill(0, pid) puts "Process ##{pid} is running" rescue Errno::EPERM # changed uid puts "No permission to query process ##{pid}!" rescue Errno::ESRCH puts "Process ##{pid} is NOT running." # or zombied rescue puts "Unable to determine status for ##{pid}: #{$!}" end end