Module: Thin::Daemonizable

Included in:
Server
Defined in:
lib/thin/daemonizing.rb

Overview

Module included in classes that can be turned into a daemon. Handle stuff like:

  • storing the PID in a file

  • redirecting output to the log file

  • changing process privileges

  • killing the process gracefully

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#log_fileObject

Returns the value of attribute log_file.



28
29
30
# File 'lib/thin/daemonizing.rb', line 28

def log_file
  @log_file
end

#pid_fileObject

Returns the value of attribute pid_file.



28
29
30
# File 'lib/thin/daemonizing.rb', line 28

def pid_file
  @pid_file
end

Class Method Details

.included(base) ⇒ Object



30
31
32
# File 'lib/thin/daemonizing.rb', line 30

def self.included(base)
  base.extend ClassMethods
end

Instance Method Details

#change_privilege(user, group = user) ⇒ Object

Change privileges of the process to the specified user and group.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/thin/daemonizing.rb', line 72

def change_privilege(user, group=user)
  log_info "Changing process privilege to #{user}:#{group}"
  
  uid, gid = Process.euid, Process.egid
  target_uid = Etc.getpwnam(user).uid
  target_gid = Etc.getgrnam(group).gid

  if uid != target_uid || gid != target_gid
    # Change PID file ownership
    File.chown(target_uid, target_gid, @pid_file) if File.exist?(@pid_file)

    # Change process ownership
    Process.initgroups(user, target_gid)
    Process::GID.change_privilege(target_gid)
    Process::UID.change_privilege(target_uid)

    # Correct environment variables
    ENV.store('USER', user)
    ENV.store('HOME', File.expand_path("~#{user}"))
  end
rescue Errno::EPERM => e
  log_info "Couldn't change user and group to #{user}:#{group}: #{e}"
end

#daemonizeObject

Turns the current script into a daemon process that detaches from the console.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/thin/daemonizing.rb', line 45

def daemonize
  raise PlatformNotSupported, 'Daemonizing is not supported on Windows'     if Thin.win?
  raise ArgumentError,        'You must specify a pid_file to daemonize' unless @pid_file
  
  remove_stale_pid_file
  
  pwd = Dir.pwd # Current directory is changed during daemonization, so store it
  
  # HACK we need to create the directory before daemonization to prevent a bug under 1.9
  #      ignoring all signals when the directory is created after daemonization.
  FileUtils.mkdir_p File.dirname(@pid_file)
  FileUtils.mkdir_p File.dirname(@log_file)
  
  Daemonize.daemonize(File.expand_path(@log_file), name)
  
  Dir.chdir(pwd)
  
  write_pid_file

  at_exit do
    log_info "Exiting!"
    remove_pid_file
  end
end

#kill(timeout = 60) ⇒ Object



38
39
40
41
42
# File 'lib/thin/daemonizing.rb', line 38

def kill(timeout = 60)
  if File.exist?(@pid_file)
    self.class.kill(@pid_file, timeout)
  end
end

#on_restart(&block) ⇒ Object

Register a proc to be called to restart the server.



97
98
99
# File 'lib/thin/daemonizing.rb', line 97

def on_restart(&block)
  @on_restart = block
end

#pidObject



34
35
36
# File 'lib/thin/daemonizing.rb', line 34

def pid
  File.exist?(pid_file) && !File.zero?(pid_file) ? open(pid_file).read.to_i : nil
end

#restartObject

Restart the server.



102
103
104
105
106
107
108
109
110
# File 'lib/thin/daemonizing.rb', line 102

def restart
  if @on_restart
    log_info 'Restarting ...'
    stop
    remove_pid_file
    @on_restart.call
    EM.next_tick { exit! }
  end
end