Class Merb::Server
In: merb-core/lib/merb-core/server.rb
Parent: Object

Server encapsulates the management of Merb daemons.

Methods

Public Class methods

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

Parameters

user<String>:The user to change the process to.
group<String>:The group to change the process to.

Alternatives

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

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 324
324:       def _change_privilege(user, group=user)
325:         Merb.logger.warn! "Changing privileges to #{user}:#{group}"
326: 
327:         uid, gid = Process.euid, Process.egid
328: 
329:         begin
330:           target_uid = Etc.getpwnam(user).uid
331:         rescue ArgumentError => e
332:           Merb.fatal!("Failed to change to user #{user}, does the user exist?", e)
333:           return false
334:         end
335: 
336:         begin
337:           target_gid = Etc.getgrnam(group).gid
338:         rescue ArgumentError => e
339:           Merb.fatal!("Failed to change to group #{group}, does the group exist?", e)
340:           return false
341:         end
342: 
343:         if (uid != target_uid) || (gid != target_gid)
344:           # Change process ownership
345:           Process.initgroups(user, target_gid)
346:           Process::GID.change_privilege(target_gid)
347:           Process::UID.change_privilege(target_uid)
348:         end
349:         true
350:       rescue Errno::EPERM => e
351:         Merb.fatal! "Permission denied for changing user:group to #{user}:#{group}.", e
352:         false
353:       end

Add trap to enter IRB on SIGINT. Process exit if second SIGINT is received.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 358
358:       def add_irb_trap
359:         Merb.trap("INT") do
360:           if @interrupted
361:             Merb.logger.warn! "Interrupt received a second time, exiting!\n"
362:             exit
363:           end
364: 
365:           @interrupted = true
366:           Merb.logger.warn! "Interrupt a second time to quit."
367:           Kernel.sleep 1.5
368:           ARGV.clear # Avoid passing args to IRB
369: 
370:           if @irb.nil?
371:             require "irb"
372:             IRB.setup(nil)
373:             @irb = IRB::Irb.new(nil)
374:             IRB.conf[:MAIN_CONTEXT] = @irb.context
375:           end
376: 
377:           Merb.trap(:INT) { @irb.signal_handle }
378:           catch(:IRB_EXIT) { @irb.eval_input }
379: 
380:           Merb.logger.warn! "Exiting from IRB mode back into server mode."
381:           @interrupted = false
382:           add_irb_trap
383:         end
384:       end

Parameters

port<~to_s>:The port to check for Merb instances on.

Returns

Boolean:True if Merb is running on the specified port.

:api: private

[Source]

    # File merb-core/lib/merb-core/server.rb, line 54
54:       def alive?(port)
55:         pidfile = pid_file(port)
56:         pid     = pid_in_file(pidfile)
57:         Process.kill(0, pid)
58:         true
59:       rescue Errno::ESRCH, Errno::ENOENT
60:         false
61:       rescue Errno::EACCES => e
62:         Merb.fatal!("You don't have access to the PID file at #{pidfile}: #{e.message}")
63:       end

Starts up Merb by running the bootloader and starting the adapter.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 168
168:       def bootup
169:         Merb.trap("TERM") { shutdown }
170: 
171:         Merb.logger.warn! "Running bootloaders..." if Merb::Config[:verbose]
172:         BootLoader.run
173:         Merb.logger.warn! "Starting Rack adapter..." if Merb::Config[:verbose]
174:         Merb.adapter.start(Merb::Config.to_hash)
175:       end

Change process user/group to those specified in Merb::Config.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 190
190:       def change_privilege
191:         if Merb::Config[:user] && Merb::Config[:group]
192:           Merb.logger.verbose! "About to change privilege to group " \
193:             "#{Merb::Config[:group]} and user #{Merb::Config[:user]}"
194:           _change_privilege(Merb::Config[:user], Merb::Config[:group])
195:         elsif Merb::Config[:user]
196:           Merb.logger.verbose! "About to change privilege to user " \
197:             "#{Merb::Config[:user]}"
198:           _change_privilege(Merb::Config[:user])
199:         else
200:           return true
201:         end
202:       end

Parameters

port<~to_s>:The port of the Merb process to daemonize.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 141
141:       def daemonize(port)
142:         Merb.logger.warn! "About to fork..." if Merb::Config[:verbose]
143:         fork do
144:           Process.setsid
145:           exit if fork
146:           Merb.logger.warn! "In #{Process.pid}" if Merb.logger
147:           File.umask 0000
148:           STDIN.reopen "/dev/null"
149:           STDOUT.reopen "/dev/null", "a"
150:           STDERR.reopen STDOUT
151:           begin
152:             Dir.chdir Merb::Config[:merb_root]
153:           rescue Errno::EACCES => e
154:             Merb.fatal! "You specified Merb root as #{Merb::Config[:merb_root]}, " \
155:               "yet the current user does not have access to it. ", e
156:           end
157:           at_exit { remove_pid_file(port) }
158:           Merb::Config[:port] = port
159:           bootup
160:         end
161:       rescue NotImplementedError => e
162:         Merb.fatal! "Daemonized mode is not supported on your platform. ", e
163:       end

Parameters

port<~to_s>:The port of the Merb process to kill.
sig<~to_s>:The signal to send to the process, the default is 9 - SIGKILL.

No Name Default Action Description 1 SIGHUP terminate process terminal line hangup 2 SIGINT terminate process interrupt program 3 SIGQUIT create core image quit program 4 SIGILL create core image illegal instruction 9 SIGKILL terminate process kill program 15 SIGTERM terminate process software termination signal 30 SIGUSR1 terminate process User defined signal 1 31 SIGUSR2 terminate process User defined signal 2

Alternatives

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

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 88
 88:       def kill(port, sig = "INT")
 89:         if sig.is_a?(Integer)
 90:           sig = Signal.list.invert[sig]
 91:         end
 92:         
 93:         Merb::BootLoader::BuildFramework.run
 94: 
 95:         # If we kill the master, then the workers should be reaped also.
 96:         if %w(main master all).include?(port)
 97:           # If a graceful exit is requested then send INT to the master process.
 98:           #
 99:           # Otherwise read pids from pid files and try to kill each process in turn.
100:           kill_pid(sig, pid_file("main")) if sig == "INT"
101:         else
102:           kill_pid(sig, pid_file(port))
103:         end
104:       end

Sends the provided signal to the process pointed at by the provided pid file. :api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 108
108:       def kill_pid(sig, file)
109:         begin
110:           pid = pid_in_file(file)
111:           Merb.logger.fatal! "Killing pid #{pid} with #{sig}"
112:           Process.kill(sig, pid)
113:           FileUtils.rm(file) if File.exist?(file)
114:         rescue Errno::EINVAL
115:           Merb.logger.fatal! "Failed to kill PID #{pid} with #{sig}: '#{sig}' is an invalid " \
116:             "or unsupported signal number."
117:         rescue Errno::EPERM
118:           Merb.logger.fatal! "Failed to kill PID #{pid} with #{sig}: Insufficient permissions."
119:         rescue Errno::ESRCH
120:           FileUtils.rm file
121:           Merb.logger.fatal! "Failed to kill PID #{pid} with #{sig}: Process is " \
122:             "deceased or zombie."
123:         rescue Errno::EACCES => e
124:           Merb.logger.fatal! e.message
125:         rescue Errno::ENOENT => e
126:           # This should not cause abnormal exit, which is why 
127:           # we do not use Merb.fatal but instead just log with max level.
128:           Merb.logger.fatal! "Could not find a PID file at #{file}. " \
129:             "Most likely the process is no longer running and the pid file was not cleaned up."
130:         rescue Exception => e
131:           if !e.is_a?(SystemExit)
132:             Merb.logger.fatal! "Failed to kill PID #{pid.inspect} with #{sig.inspect}: #{e.message}"
133:           end
134:         end
135:       end

Gets the pid file for the specified port/socket.

Parameters

port<~to_s>:The port/socket 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/socket value to the path.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 290
290:       def pid_file(port)
291:         pidfile = Merb::Config[:pid_file] || (Merb.log_path / "merb.%s.pid")
292:         pidfile % port
293:       end

Get a list of the pid files.

Returns

Array:List of pid file paths. If not running clustered, the array contains a single path.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 302
302:       def pid_files
303:         if Merb::Config[:pid_file]
304:           if Merb::Config[:cluster]
305:             Dir[Merb::Config[:pid_file] % "*"]
306:           else
307:             [ Merb::Config[:pid_file] ]
308:           end
309:         else
310:           Dir[Merb.log_path / "merb.*.pid"]
311:         end
312:        end

:api: private

[Source]

    # File merb-core/lib/merb-core/server.rb, line 66
66:       def pid_in_file(pidfile)
67:         File.read(pidfile).chomp.to_i
68:       end

Delete the pidfile for the specified port/socket.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 245
245:       def remove_pid(port)
246:         FileUtils.rm(pid_file(port)) if File.file?(pid_file(port))
247:       end

Removes a PID file used by the server from the filesystem. This uses :pid_file options from configuration when provided or merb.<port/socket>.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[:pid_file] has been specified, that will be used instead of the port/socket based PID file.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 217
217:       def remove_pid_file(port)
218:         pidfile = pid_file(port)
219:         if File.exist?(pidfile)
220:           Merb.logger.warn! "Removing pid file #{pidfile} (port/socket: #{port})..."
221:           FileUtils.rm(pidfile)
222:         end
223:       end

Shut down Merb, reap any workers if necessary.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 180
180:       def shutdown(status = 0)
181:         # reap_workers does exit but may not be called...
182:         Merb::BootLoader::LoadClasses.reap_workers(status) if Merb::Config[:fork_for_class_load]
183:         # which is why we exit explicitly here
184:         exit(status)
185:       end

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[:daemonize] is true.

:api: private

[Source]

    # File merb-core/lib/merb-core/server.rb, line 23
23:       def start(port, cluster=nil)
24: 
25:         @port = port
26:         @cluster = cluster
27: 
28:         if Merb::Config[:daemonize]
29:           pidfile = pid_file(port)
30:           pid = File.read(pidfile).chomp.to_i if File.exist?(pidfile)
31: 
32:           unless alive?(@port)
33:             remove_pid_file(@port)
34:             Merb.logger.warn! "Daemonizing..." if Merb::Config[:verbose]
35:             daemonize(@port)
36:           else
37:             Merb.fatal! "Merb is already running on port #{port}.\n" \
38:               "\e[0m   \e[1;31;47mpid file: \e[34;47m#{pidfile}" \
39:               "\e[1;31;47m, process id is \e[34;47m#{pid}."
40:           end
41:         else
42:           bootup
43:         end
44:       end

Stores a PID file on the filesystem. This uses :pid_file options from configuration when provided or merb.<port/socket>.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[:pid_file] has been specified, that will be used instead of the port/socket based PID file.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 262
262:       def store_details(port = nil)
263:         file = pid_file(port)
264:         begin
265:           FileUtils.mkdir_p(File.dirname(file))
266:         rescue Errno::EACCES => e
267:           Merb.fatal! "Failed to store Merb logs in #{File.dirname(file)}, " \
268:             "permission denied. ", e
269:         end
270:         Merb.logger.warn! "Storing pid #{Process.pid} file to #{file}..." if Merb::Config[:verbose]
271:         begin
272:           File.open(file, 'w'){ |f| f.write(Process.pid.to_s) }
273:         rescue Errno::EACCES => e
274:           Merb.fatal! "Failed to access #{file}, permission denied.", e
275:         end
276:       end

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[:pid_file] has been specified, that will be used instead of the port/socket based PID file.

:api: private

[Source]

     # File merb-core/lib/merb-core/server.rb, line 238
238:       def store_pid(port)
239:         store_details(port)
240:       end

[Validate]