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

._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.



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/merb-core/server.rb', line 233

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

.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)


63
64
65
66
67
68
69
70
71
72
73
# File 'lib/merb-core/server.rb', line 63

def alive?(port)
  puts "About to check if port #{port} is alive..." if Merb::Config[:verbose]
  pidfile = pid_file(port)
  puts "Pidfile is #{pidfile}..." if Merb::Config[:verbose]
  pid = IO.read(pidfile).chomp.to_i
  puts "Process id is #{pid}" if Merb::Config[:verbose]
  Process.kill(0, pid)
  true
rescue
  false
end

.change_privilegeObject



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

def change_privilege
  if Merb::Config[:user]
    if Merb::Config[:group]
      puts "About to change privilege to group #{Merb::Config[:group]} and user #{Merb::Config[:user]}" if Merb::Config[:verbose]
      _change_privilege(Merb::Config[:user], Merb::Config[:group])
    else
      puts "About to change privilege to user #{Merb::Config[:user]}" if Merb::Config[:verbose]
      _change_privilege(Merb::Config[:user])
    end
  end
end

.daemonize(port) ⇒ Object

Parameters

port<~to_s>

The port of the Merb process to daemonize.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/merb-core/server.rb', line 112

def daemonize(port)
  puts "About to fork..." if Merb::Config[:verbose]
  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
    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.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/merb-core/server.rb', line 82

def kill(port, sig=9)
  Merb::BootLoader::BuildFramework.run
  begin
    pidfiles = port == "all" ?
      pid_files : [ pid_file(port) ]

    pidfiles.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

.pid_file(port) ⇒ Object

Gets the pid file for the specified port.

Parameters

port<~to_s>

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

Returns

String

Location of pid file for specified port. If clustered and pid_file option is specified, it adds the port value to the path.



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/merb-core/server.rb', line 188

def pid_file(port)
  if Merb::Config[:pid_file]
    pidfile = Merb::Config[:pid_file]
    if Merb::Config[:cluster]
      ext = File.extname(Merb::Config[:pid_file])
      base = File.basename(Merb::Config[:pid_file], ext)
      dir = File.dirname(Merb::Config[:pid_file])
      File.join(dir, "#{base}.#{port}#{ext}")
    else
      Merb::Config[:pid_file]
    end
  else
    pidfile = Merb.log_path / "merb.#{port}.pid"
    Merb.log_path / "merb.#{port}.pid"
  end
end

.pid_filesObject

Get a list of the pid files.

Returns

Array

List of pid file paths. If not clustered, array contains a single path.



210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/merb-core/server.rb', line 210

def pid_files
 if Merb::Config[:pid_file]
   if Merb::Config[:cluster]
     ext = File.extname(Merb::Config[:pid_file])
     base = File.basename(Merb::Config[:pid_file], ext)
     dir = File.dirname(Merb::Config[:pid_file])
     Dir[dir / "#{base}.*#{ext}"]
   else
     [ Merb::Config[:pid_file] ]
   end
 else
   Dir[Merb.log_path / "merb.*.pid"]
 end
end

.remove_pid_file(port) ⇒ Object

Removes a PID file used by the server from the filesystem. This uses :pid_file options from configuration when provided or merb.<port>.pid in log directory by default.

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.



153
154
155
156
157
# File 'lib/merb-core/server.rb', line 153

def remove_pid_file(port)
  pidfile = pid_file(port)
  puts "Removing pid file #{pidfile} (port is #{port})..."
  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
46
47
48
49
50
51
52
53
54
55
# 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|
      pidfile = pid_file(port)
      pid = IO.read(pidfile).chomp.to_i if File.exist?(pidfile)

      unless alive?(port)
        remove_pid_file(port)
        puts "Starting merb server on port #{port}, pid file: #{pidfile} and process id is #{pid}" if Merb::Config[:verbose]
        daemonize(port)
      else
        raise "Merb is already running: port is #{port}, pid file: #{pidfile}, process id is #{pid}"
      end
    end
  elsif Merb::Config[:daemonize]
    pidfile = pid_file(port)
    pid = IO.read(pidfile).chomp.to_i if File.exist?(pidfile)

    unless alive?(@port)
      remove_pid_file(@port)
      puts "Daemonizing..." if Merb::Config[:verbose]
      daemonize(@port)
    else
      raise "Merb is already running: port is #{port}, pid file: #{pidfile}, process id is #{pid}"
    end
  else
    trap('TERM') { exit }
    trap('INT') { puts "\nExiting"; exit }
    puts "Running bootloaders..." if Merb::Config[:verbose]
    BootLoader.run
    puts "Starting Rack adapter..." if Merb::Config[:verbose]
    Merb.adapter.start(Merb::Config.to_hash)
  end
end

.store_pid(port) ⇒ Object

Stores a PID file on the filesystem. This uses :pid_file options from configuration when provided or merb.<port>.pid in log directory by default.

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.



170
171
172
173
174
175
176
# File 'lib/merb-core/server.rb', line 170

def store_pid(port)
  pidfile = pid_file(port)
  puts "Storing pid file to #{pidfile}..."
  FileUtils.mkdir_p(File.dirname(pidfile)) unless File.directory?(File.dirname(pidfile))
  puts "Created directory, writing process id..." if Merb::Config[:verbose]
  File.open(pidfile, 'w'){ |f| f.write("#{Process.pid}") }
end