관리-도구
편집 파일: builtin_engine.rb
# encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2018 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. require 'etc' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'standalone/control_utils' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'utils/json' module PhusionPassenger module Standalone class StartCommand module BuiltinEngine private def start_engine_real Standalone::ControlUtils.require_daemon_controller @engine = DaemonController.new(build_daemon_controller_options) start_engine_no_create end def wait_until_engine_has_exited read_and_delete_report_file! if PlatformInfo.supports_flock? lock = DaemonController::LockFile.new(@watchdog_lock_file_path) lock.shared_lock do # Do nothing end else pid = @engine.pid while process_is_alive?(pid) sleep 1 end end end def start_engine_no_create begin @engine.start rescue DaemonController::AlreadyStarted begin pid = @engine.pid rescue SystemCallError, IOError pid = nil end if @can_remove_working_dir FileUtils.remove_entry_secure(@working_dir) @can_remove_working_dir = false end if pid abort "#{PROGRAM_NAME} Standalone is already running on PID #{pid}." else abort "#{PROGRAM_NAME} Standalone is already running." end rescue DaemonController::StartError => e abort "Could not start the server engine:\n#{e}" end end def build_daemon_controller_options ping_spec = lambda do File.exist?(report_file_path) && File.stat(report_file_path).size > 0 end command = "" if !@options[:envvars].empty? command = "env " @options[:envvars].each_pair do |name, value| command << "#{Shellwords.escape name}=#{Shellwords.escape value} " end end command << "#{@agent_exe} watchdog"; command << " --passenger-root #{Shellwords.escape PhusionPassenger.install_spec}" command << " --daemonize" command << " --no-user-switching" command << " --no-delete-pid-file" command << " --cleanup-pidfile #{Shellwords.escape @working_dir}/temp_dir_toucher.pid" command << " --report-file #{Shellwords.escape @working_dir}/report.json" command << " --ctl prestart_urls=#{Shellwords.escape prestart_urls_json}" add_param(command, :user, "--user") add_param(command, :log_file, "--log-file") add_param(command, :pid_file, "--pid-file") add_param(command, :instance_registry_dir, "--instance-registry-dir") add_param(command, :spawn_dir, "--spawn-dir") add_param(command, :data_buffer_dir, "--data-buffer-dir") add_param(command, :log_level, "--log-level") add_flag_param(command, :disable_log_prefix, "--disable-log-prefix") @options[:ctls].each do |ctl| command << " --ctl #{Shellwords.escape ctl}" end if @options[:user] command << " --default-user #{Shellwords.escape @options[:user]}" else user = Etc.getpwuid(Process.uid).name begin group = Etc.getgrgid(Process.gid) rescue ArgumentError # Do nothing. On Heroku, it's normal that the group # database is broken. else command << " --default-group #{Shellwords.escape group.name}" end command << " --default-user #{Shellwords.escape user}" end # Arguments for supervised agent are between --BC/--EC and --BU/--EU command << " --BC" # The builtin engine cannot be used in combination with Mass Deployment, # so we know @apps always has 1 app. command << " --listen #{listen_address(@apps[0])}" command << " --no-graceful-exit" add_param(command, :socket_backlog, "--socket-backlog") add_param(command, :environment, "--environment") add_param(command, :app_type, "--app-type") add_param(command, :startup_file, "--startup-file") add_param(command, :app_start_command, "--app-start-command") add_param(command, :spawn_method, "--spawn-method") add_param(command, :restart_dir, "--restart-dir") add_param(command, :custom_error_page, "--custom-error-page") if @options.has_key?(:friendly_error_pages) if @options[:friendly_error_pages] command << " --force-friendly-error-pages" else command << " --disable-friendly-error-pages" end end if @options[:turbocaching] == false command << " --disable-turbocaching" end if @options[:abort_websockets_on_process_shutdown] == false command << " --no-abort-websockets-on-process-shutdown" end add_param(command, :force_max_concurrent_requests_per_process, "--force-max-concurrent-requests-per-process") add_flag_param(command, :load_shell_envvars, "--load-shell-envvars") add_flag_param(command, :preload_bundler, "--preload-bundler") add_param(command, :max_pool_size, "--max-pool-size") add_param(command, :min_instances, "--min-instances") add_param(command, :pool_idle_time, "--pool-idle-time") add_param(command, :max_preloader_idle_time, "--max-preloader-idle-time") add_param(command, :max_request_queue_size, "--max-request-queue-size") add_enterprise_param(command, :concurrency_model, "--concurrency-model") add_enterprise_param(command, :thread_count, "--app-thread-count") add_param(command, :max_requests, "--max-requests") add_enterprise_param(command, :max_request_time, "--max-request-time") add_enterprise_param(command, :memory_limit, "--memory-limit") add_enterprise_flag_param(command, :rolling_restarts, "--rolling-restarts") add_enterprise_flag_param(command, :resist_deployment_errors, "--resist-deployment-errors") add_enterprise_flag_param(command, :debugger, "--debugger") add_flag_param(command, :sticky_sessions, "--sticky-sessions") add_param(command, :vary_turbocache_by_cookie, "--vary-turbocache-by-cookie") add_param(command, :sticky_sessions_cookie_name, "--sticky-sessions-cookie-name") add_param(command, :sticky_sessions_cookie_attributes, "--sticky-sessions-cookie-attributes") add_param(command, :ruby, "--ruby") add_param(command, :python, "--python") add_param(command, :nodejs, "--nodejs") add_param(command, :meteor_app_settings, "--meteor-app-settings") add_param(command, :core_file_descriptor_ulimit, "--core-file-descriptor-ulimit") add_param(command, :app_file_descriptor_ulimit, "--app-file-descriptor-ulimit") add_flag_param(command, :disable_security_update_check, "--disable-security-update-check") add_param(command, :security_update_check_proxy, "--security-update-check-proxy") add_flag_param(command, :disable_anonymous_telemetry, "--disable-anonymous-telemetry") add_param(command, :anonymous_telemetry_proxy, "--anonymous-telemetry-proxy") command << " #{Shellwords.escape(@apps[0][:root])}" command << " --EC --BU" add_param(command, :core_file_descriptor_ulimit, "--core-file-descriptor-ulimit") return { :identifier => "#{AGENT_EXE} watchdog", :start_command => command, :ping_command => ping_spec, :pid_file => @options[:pid_file], :log_file => @options[:log_file], :timeout => 25 } end def listen_address(options = @options, for_ping_port = false) if options[:socket_file] return "unix:#{options[:socket_file]}" else return "tcp://" + compose_ip_and_port(options[:address], options[:port]) end end def prestart_urls_json PhusionPassenger::Utils::JSON.generate([listen_url(@apps[0])]) end def add_param(command, option_name, param_name) if value = @options[option_name] command << " #{param_name} #{Shellwords.escape value.to_s}" end end def add_flag_param(command, option_name, param_name) if value = @options[option_name] command << " #{param_name}" end end def add_enterprise_param(command, option_name, param_name) if value = @options[option_name] abort "The '#{option_name}' feature is only available in #{PROGRAM_NAME} " + "Enterprise. You are currently running the open source #{PROGRAM_NAME}. " + "Please learn more about and/or buy #{PROGRAM_NAME} Enterprise at " + "https://www.phusionpassenger.com/features#premium-features" end end def add_enterprise_flag_param(command, option_name, param_name) if value = @options[option_name] abort "The '#{option_name}' feature is only available in #{PROGRAM_NAME} " + "Enterprise. You are currently running the open source #{PROGRAM_NAME}. " + "Please learn more about and/or buy #{PROGRAM_NAME} Enterprise at " + "https://www.phusionpassenger.com/features#premium-features" end end def report_file_path @report_file_path ||= "#{@working_dir}/report.json" end def read_and_delete_report_file! report = File.open(report_file_path, "r:utf-8") do |f| Utils::JSON.parse(f.read) end # The report file may contain sensitive information, so delete it. File.unlink(report_file_path) @watchdog_lock_file_path = report["instance_dir"] + "/lock" end def process_is_alive?(pid) begin Process.kill(0, pid) true rescue Errno::ESRCH false rescue SystemCallError => e true end end ##################### end # module BuiltinEngine end # module StartCommand end # module Standalone end # module PhusionPassenger