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.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/merb-core/server.rb', line 258

def _change_privilege(user, group=user)

  Merb.logger.warn! "Changing privileges to #{user}:#{group}"

  uid, gid = Process.euid, Process.egid
  
  begin
    target_uid = Etc.getpwnam(user).uid
  rescue ArgumentError => e
    Merb.fatal!(
      "You tried to use user #{user}, but no such user was found", e)
    return false
  end
  
  begin
    target_gid = Etc.getgrnam(group).gid
  rescue ArgumentError => e
    Merb.fatal!(
      "You tried to use group #{group}, but no such group was found", e)
    return false
  end

  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
  true
rescue Errno::EPERM => e
  Merb.fatal! "Couldn't change user and group to #{user}:#{group}", e
  false
end

.add_irb_trapObject



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/merb-core/server.rb', line 292

def add_irb_trap
  trap('INT') do
    if @interrupted
      puts "Exiting\n"
      exit
    end
    
    @interrupted = true
    puts "Interrupt a second time to quit"
    Kernel.sleep 1.5
    ARGV.clear # Avoid passing args to IRB

    if @irb.nil?
      require 'irb'
      IRB.setup(nil)
      @irb = IRB::Irb.new(nil)
      IRB.conf[:MAIN_CONTEXT] = @irb.context
    end

    trap(:INT) { @irb.signal_handle }
    catch(:IRB_EXIT) { @irb.eval_input }

    puts "Exiting IRB mode, back in server mode"
    @interrupted = false
    add_irb_trap
  end
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)


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

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 = File.read(pidfile).chomp.to_i
  puts "Process id is #{pid}" if Merb::Config[:verbose]
  Process.kill(0, pid)
  true
rescue Errno::ESRCH, Errno::ENOENT
  false
rescue Errno::EACCES => e
  Merb.fatal!("You don't have access to the PID file at #{pidfile}.", e)
end

.bootupObject



145
146
147
148
149
150
151
152
# File 'lib/merb-core/server.rb', line 145

def bootup
  trap('TERM') { 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

.change_privilegeObject



154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/merb-core/server.rb', line 154

def change_privilege
  if Merb::Config[:user] && Merb::Config[:group]
    Merb.logger.verbose! "About to change privilege to group " \
      "#{Merb::Config[:group]} and user #{Merb::Config[:user]}"
    _change_privilege(Merb::Config[:user], Merb::Config[:group])
  elsif Merb::Config[:user]
    Merb.logger.verbose! "About to change privilege to user " \
      "#{Merb::Config[:user]}"
    _change_privilege(Merb::Config[:user])
  else
    return true
  end
end

.daemonize(port) ⇒ Object

Parameters

port<~to_s>

The port of the Merb process to daemonize.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/merb-core/server.rb', line 121

def daemonize(port)
  puts "About to fork..." if Merb::Config[:verbose]
  fork do
    Process.setsid
    exit if fork
    Merb.logger.warn! "In #{Process.pid}" if Merb.logger
    File.umask 0000
    STDIN.reopen "/dev/null"
    STDOUT.reopen "/dev/null", "a"
    STDERR.reopen STDOUT
    begin
      Dir.chdir Merb::Config[:merb_root]
    rescue Errno::EACCES => e
      Merb.fatal! "You specified #{Merb::Config[:merb_root]} " \
        "as the Merb root, but you did not have access to it.", e
    end
    at_exit { remove_pid_file(port) }
    Merb::Config[:port] = port
    bootup
  end
rescue NotImplementedError => e
  Merb.fatal! "Daemonized mode is not supported on your platform", e
end

.kill(port, sig = "INT") ⇒ 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.



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

def kill(port, sig="INT")
  Merb::BootLoader::BuildFramework.run
  if sig == 9 && port == "main"
    kill_pid("INT", pid_file("main"))
    Dir["#{Merb.log_path}" / "*.pid"].each do |file|
      kill_pid(9, file)
    end
  else
    kill_pid(sig, pid_file(port))
  end

  if sig.is_a?(Integer)
    sig = Signal.list.invert[sig]
  end

  if sig == "KILL" && port == "main"
    Merb.fatal! "Killed all PIDs with signal KILL"
  else
    Merb.fatal! "Killed #{port} with signal #{sig}"
  end
end

.kill_pid(sig, file) ⇒ Object



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

def kill_pid(sig, file)
  begin
    pid = File.read(file).chomp.to_i
    Merb.logger.warn! "Killing pid #{pid}"
    Process.kill(sig, pid)
    FileUtils.rm(file) if File.exist?(file)
  rescue Errno::EINVAL
    Merb.fatal! "Failed to kill PID #{pid}: '#{sig}' is an invalid " \
      "or unsupported signal number."
  rescue Errno::EPERM
    Merb.fatal! "Failed to kill PID #{pid}: Insufficient permissions."
  rescue Errno::ESRCH
    FileUtils.rm file
    Merb.fatal! "Failed to kill PID #{pid}: Process is " \
      "deceased or zombie."
  rescue Errno::EACCES => e
    Merb.fatal! e.message, e
  rescue Errno::ENOENT => e
    Merb.fatal! "Could not find a PID file at #{file}", e
  rescue Exception => e
    if !e.is_a?(SystemExit)
      Merb.fatal! "Failed to kill PID #{pid}", e
    end
  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.



228
229
230
231
# File 'lib/merb-core/server.rb', line 228

def pid_file(port)
  pidfile = Merb::Config[:pid_file] || (Merb.log_path / "merb.%s.pid")
  pidfile % port
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.



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

def pid_files
 if Merb::Config[:pid_file]
   if Merb::Config[:cluster]
     Dir[Merb::Config[:pid_file] % "*"]
   else
     [ Merb::Config[:pid_file] ]
   end
 else
   Dir[Merb.log_path / "merb.*.pid"]
 end
end

.remove_pid(port) ⇒ Object



202
203
204
# File 'lib/merb-core/server.rb', line 202

def remove_pid(port)
  FileUtils.rm(pid_file(port)) if File.file?(pid_file(port))
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.



179
180
181
182
183
184
185
# File 'lib/merb-core/server.rb', line 179

def remove_pid_file(port)
  pidfile = pid_file(port)
  if File.exist?(pidfile)
    puts "Removing pid file #{pidfile} (port is #{port})..."
    FileUtils.rm(pidfile)
  end
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.



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

def start(port, cluster=nil)
  
  @port = port
  @cluster = cluster        

  if Merb::Config[:daemonize]
    pidfile = pid_file(port)
    pid = File.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
      Merb.fatal! "Merb is already running on port #{port}.\n" \
        "\e[0m   \e[1;31;47mpid file: \e[34;47m#{pidfile}" \
        "\e[1;31;47m, process id is \e[34;47m#{pid}."
    end
  else
    bootup
  end
end

.store_details(port = nil) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
# File 'lib/merb-core/server.rb', line 206

def store_details(port = nil)
  file = pid_file(port)
  begin
    FileUtils.mkdir_p(File.dirname(file))
  rescue Errno::EACCES => e
    Merb.fatal! "You tried to store Merb logs in #{File.dirname(file)}, " \
      "but you did not have access.", e
  end
  Merb.logger.warn! "Storing #{type} file to #{file}..." if Merb::Config[:verbose]
  File.open(file, 'w'){ |f| f.write(Process.pid.to_s) }
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.



198
199
200
# File 'lib/merb-core/server.rb', line 198

def store_pid(port)
  store_details(port)
end