Class: Merb::Server

Inherits:
Object show all
Defined in:
lib/merb-core/server.rb

Overview

Server encapsulates the management of Merb daemons.

Class Method Summary collapse

Class Method Details

.alive?(port) ⇒ Boolean

Parameters

port<~to_s>

The port to check for Merb instances on.

Returns

Boolean

True if Merb is running on the specified port.

Returns:

  • (Boolean)


53
54
55
56
57
58
59
60
# File 'lib/merb-core/server.rb', line 53

def alive?(port)
  f = "#{Merb.log_path}" / "merb.#{port}.pid"
  pid = IO.read(f).chomp.to_i
  Process.kill(0, pid)
  true
rescue
  false
end

.change_privilege(user, group = user) ⇒ Object

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

Parameters

user<String>

The user who should own the server process.

group<String>

The group who should own the server process.

Alternatives

If group is left out, the user will be used as the group.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/merb-core/server.rb', line 165

def change_privilege(user, group=user)
  
  puts "Changing privileges 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 process ownership
    Process.initgroups(user, target_gid)
    Process::GID.change_privilege(target_gid)
    Process::UID.change_privilege(target_uid)
  end
rescue Errno::EPERM => e
  puts "Couldn't change user and group to #{user}:#{group}: #{e}"
end

.daemonize(port) ⇒ Object

Parameters

port<~to_s>

The port of the Merb process to daemonize.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/merb-core/server.rb', line 96

def daemonize(port)
  fork do
    Process.setsid
    exit if fork
    File.umask 0000
    STDIN.reopen "/dev/null"
    STDOUT.reopen "/dev/null", "a"
    STDERR.reopen STDOUT
    trap("TERM") { exit }
    Dir.chdir Merb::Config[:merb_root]
    at_exit { remove_pid_file(port) }
    Merb::Config[:port] = port
    if Merb::Config[:user]
      if Merb::Config[:group]
        change_privilege(Merb::Config[:user], Merb::Config[:group])
      else
        change_privilege(Merb::Config[:user])
      end    
    end  
    BootLoader.run
    Merb.adapter.start(Merb::Config.to_hash)
  end
end

.kill(port, sig = 9) ⇒ Object

Parameters

port<~to_s>

The port of the Merb process to kill.

sig<~to_s>

The signal to send to the process. Defaults to 9.

Alternatives

If you pass “all” as the port, the signal will be sent to all Merb processes.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/merb-core/server.rb', line 69

def kill(port, sig=9)
  Merb::BootLoader::BuildFramework.run
  begin
    Dir[Merb.log_path/ "merb.#{port == 'all' ? '*' : port }.pid"].each do |f|
      pid = IO.read(f).chomp.to_i
      begin
        Process.kill(sig, pid)
        FileUtils.rm(f) if File.exist?(f)
        puts "killed PID #{pid} with signal #{sig}"
      rescue Errno::EINVAL
        puts "Failed to kill PID #{pid}: '#{sig}' is an invalid or unsupported signal number."
      rescue Errno::EPERM
        puts "Failed to kill PID #{pid}: Insufficient permissions."
      rescue Errno::ESRCH
        puts "Failed to kill PID #{pid}: Process is deceased or zombie."
        FileUtils.rm f
      rescue Exception => e
        puts "Failed to kill PID #{pid}: #{e.message}"
      end
    end
  ensure  
    exit
  end
end

.remove_pid_file(port) ⇒ Object

Removes a PID file from the filesystem.

Parameters

port<~to_s>

The port of the Merb process to whom the the PID file belongs to.

Alternatives

If Merb::Config has been specified, that will be used instead of the port based PID file.



129
130
131
132
133
134
135
136
# File 'lib/merb-core/server.rb', line 129

def remove_pid_file(port)
  if Merb::Config[:pid_file]
    pidfile = Merb::Config[:pid_file]
  else
    pidfile = Merb.log_path / "merb.#{port}.pid"
  end
  FileUtils.rm(pidfile) if File.exist?(pidfile)
end

.start(port, cluster = nil) ⇒ Object

Start a Merb server, in either foreground, daemonized or cluster mode.

Parameters

port<~to_i>

The port to which the first server instance should bind to. Subsequent server instances bind to the immediately following ports.

cluster<~to_i>

Number of servers to run in a cluster.

Alternatives

If cluster is left out, then one process will be started. This process will be daemonized if Merb::Config is true.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/merb-core/server.rb', line 20

def start(port, cluster=nil)
  @port = port
  @cluster = cluster
  if @cluster
    @port.to_i.upto(@port.to_i + @cluster.to_i-1) do |port|
      unless alive?(port)
        remove_pid_file(port)
        puts "Starting merb server on port: #{port}"
        daemonize(port)
      else
        raise "Merb is already running on port: #{port}"
      end
    end   
  elsif Merb::Config[:daemonize]
    unless alive?(@port)  
      remove_pid_file(@port)
      daemonize(@port)
    else
      raise "Merb is already running on port: #{port}"
    end
  else
    trap('TERM') { exit }
    BootLoader.run
    Merb.adapter.start(Merb::Config.to_hash)
  end
end

.store_pid(port) ⇒ Object

Stores a PID file on the filesystem.

Parameters

port<~to_s>

The port of the Merb process to whom the the PID file belongs to.

Alternatives

If Merb::Config has been specified, that will be used instead of the port based PID file.



147
148
149
150
151
152
153
154
155
# File 'lib/merb-core/server.rb', line 147

def store_pid(port)
  FileUtils.mkdir_p(Merb.log_path) unless File.directory?(Merb.log_path)
  if Merb::Config[:pid_file]
    pidfile = Merb::Config[:pid_file]
  else
    pidfile = Merb.log_path / "merb.#{port}.pid"
  end
  File.open(pidfile, 'w'){ |f| f.write("#{Process.pid}") }
end