pipemaster 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +0 -0
- data/Gemfile +5 -0
- data/LICENSE +55 -0
- data/README.rdoc +61 -0
- data/Rakefile +51 -0
- data/bin/pipemaster +100 -0
- data/lib/pipemaster.rb +8 -0
- data/lib/pipemaster/client.rb +121 -0
- data/lib/pipemaster/configurator.rb +228 -0
- data/lib/pipemaster/server.rb +164 -0
- data/lib/pipemaster/worker.rb +4 -0
- data/pipemaster.gemspec +18 -0
- data/test/test_helper.rb +268 -0
- data/test/unit/test_configurator.rb +151 -0
- data/test/unit/test_server.rb +84 -0
- metadata +84 -0
data/CHANGELOG
ADDED
File without changes
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
Pipemaster is copyrighted free software by all contributors, see logs in
|
2
|
+
revision control for names and email addresses of all of them. You can
|
3
|
+
redistribute it and/or modify it under either the terms of the
|
4
|
+
{GPL2}[http://www.gnu.org/licenses/gpl-2.0.txt] (see link:COPYING) or
|
5
|
+
the conditions below:
|
6
|
+
|
7
|
+
1. You may make and give away verbatim copies of the source form of the
|
8
|
+
software without restriction, provided that you duplicate all of the
|
9
|
+
original copyright notices and associated disclaimers.
|
10
|
+
|
11
|
+
2. You may modify your copy of the software in any way, provided that
|
12
|
+
you do at least ONE of the following:
|
13
|
+
|
14
|
+
a) place your modifications in the Public Domain or otherwise make them
|
15
|
+
Freely Available, such as by posting said modifications to Usenet or an
|
16
|
+
equivalent medium, or by allowing the author to include your
|
17
|
+
modifications in the software.
|
18
|
+
|
19
|
+
b) use the modified software only within your corporation or
|
20
|
+
organization.
|
21
|
+
|
22
|
+
c) rename any non-standard executables so the names do not conflict with
|
23
|
+
standard executables, which must also be provided.
|
24
|
+
|
25
|
+
d) make other distribution arrangements with the author.
|
26
|
+
|
27
|
+
3. You may distribute the software in object code or executable
|
28
|
+
form, provided that you do at least ONE of the following:
|
29
|
+
|
30
|
+
a) distribute the executables and library files of the software,
|
31
|
+
together with instructions (in the manual page or equivalent) on where
|
32
|
+
to get the original distribution.
|
33
|
+
|
34
|
+
b) accompany the distribution with the machine-readable source of the
|
35
|
+
software.
|
36
|
+
|
37
|
+
c) give non-standard executables non-standard names, with
|
38
|
+
instructions on where to get the original software distribution.
|
39
|
+
|
40
|
+
d) make other distribution arrangements with the author.
|
41
|
+
|
42
|
+
4. You may modify and include the part of the software into any other
|
43
|
+
software (possibly commercial). But some files in the distribution
|
44
|
+
are not written by the author, so that they are not under this terms.
|
45
|
+
|
46
|
+
5. The scripts and library files supplied as input to or produced as
|
47
|
+
output from the software do not automatically fall under the
|
48
|
+
copyright of the software, but belong to whomever generated them,
|
49
|
+
and may be sold commercially, and may be aggregated with this
|
50
|
+
software.
|
51
|
+
|
52
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
53
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
54
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
55
|
+
PURPOSE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
You pipe from here, to a process that runs over there. Pipemaster forks,
|
2
|
+
redirects and runs your command.
|
3
|
+
|
4
|
+
== Why
|
5
|
+
|
6
|
+
I've got a short task I want to perform. To validate an incoming email, parse
|
7
|
+
the message and store the results in the database(*). I setup Postfix to pipe
|
8
|
+
incoming emails into a Ruby script.
|
9
|
+
|
10
|
+
Ruby processes are fairly cheap, until you get into loading the mail library,
|
11
|
+
the database library, the ORM, the application logic, the ... you get the
|
12
|
+
picture.
|
13
|
+
|
14
|
+
I use Pipemaster to fire up the main process once, and the Pipemaster client to
|
15
|
+
run commands on that server. Still have to make sure the code is light and
|
16
|
+
fast, but I did eliminate the significant initialization overhead.
|
17
|
+
|
18
|
+
As you can guess, Pipemaster supports piping input and output streams, and uses
|
19
|
+
forking (sorry Windows; for JRuby see Nailgun).
|
20
|
+
|
21
|
+
* Processing emails as they come allows the application to reject unauthorized
|
22
|
+
senders immediately by replying with an SMTP code. The alternative, accepting
|
23
|
+
the email and later on sending a bounce, leads to backscatter (see
|
24
|
+
http://en.wikipedia.org/wiki/Backscatter_(e-mail)).
|
25
|
+
|
26
|
+
|
27
|
+
== Using Pipemaster
|
28
|
+
|
29
|
+
Step 1: Create a Pipemaster file. For example:
|
30
|
+
|
31
|
+
#!highlight/ruby
|
32
|
+
command :echo do |*args|
|
33
|
+
first, rest = $stdin.read.split
|
34
|
+
$stdout << [first, args, rest].flatten.join(" ")
|
35
|
+
end
|
36
|
+
|
37
|
+
Step 2: Fire up the Pipemaster server:
|
38
|
+
|
39
|
+
$ pipemaster --server
|
40
|
+
I, [2010-02-18T12:57:57.739230 #5460] INFO -- : master process ready
|
41
|
+
I, [2010-02-18T12:57:57.739606 #5460] INFO -- : listening on addr=127.0.0.1:7887 fd=3
|
42
|
+
|
43
|
+
Step 3: For a new shell, execute a command:
|
44
|
+
|
45
|
+
$ echo "Stand down!" | ruby -Ilib bin/pipemaster echo upside
|
46
|
+
Stand upside down!
|
47
|
+
|
48
|
+
|
49
|
+
== License
|
50
|
+
|
51
|
+
Pipemaster is copyright of Assaf Arkin. It is based on the awesome Unicorn Web
|
52
|
+
server and therefore uses the same license.
|
53
|
+
|
54
|
+
Unicorn is copyright 2009 by all contributors (see logs in git).
|
55
|
+
It is based on Mongrel and carries the same license.
|
56
|
+
|
57
|
+
Mongrel is copyright 2007 Zed A. Shaw and contributors. It is licensed
|
58
|
+
under the Ruby license and the GPL2. See the included LICENSE file for
|
59
|
+
details.
|
60
|
+
|
61
|
+
Pipemaster is 100% Free Software.
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
|
3
|
+
spec = Gem::Specification.load(File.expand_path("pipemaster.gemspec", File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
desc "Push new release to gemcutter and git tag"
|
6
|
+
task :push do
|
7
|
+
sh "git push"
|
8
|
+
puts "Tagging version #{spec.version} .."
|
9
|
+
sh "git tag v#{spec.version}"
|
10
|
+
sh "git push --tag"
|
11
|
+
puts "Building and pushing gem .."
|
12
|
+
sh "gem build #{spec.name}.gemspec"
|
13
|
+
sh "gem push #{spec.name}-#{spec.version}.gem"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Install #{spec.name} locally"
|
17
|
+
task :install do
|
18
|
+
sh "gem build #{spec.name}.gemspec"
|
19
|
+
sudo = "sudo" unless File.writable?( Gem::ConfigMap[:bindir])
|
20
|
+
sh "#{sudo} gem install #{spec.name}-#{spec.version}.gem"
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
task :default=>:test
|
25
|
+
desc "Run all tests using Redis mock (also default task)"
|
26
|
+
Rake::TestTask.new do |task|
|
27
|
+
task.test_files = FileList['test/unit/test_*.rb']
|
28
|
+
if Rake.application.options.trace
|
29
|
+
#task.warning = true
|
30
|
+
task.verbose = true
|
31
|
+
elsif Rake.application.options.silent
|
32
|
+
task.ruby_opts << "-W0"
|
33
|
+
else
|
34
|
+
task.verbose = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
begin
|
40
|
+
require "yard"
|
41
|
+
YARD::Rake::YardocTask.new(:yardoc) do |task|
|
42
|
+
task.files = FileList["lib/**/*.rb"]
|
43
|
+
task.options = "--output", "html", "--title", "Pipemaster #{spec.version}", "--main", "README.rdoc", "--files", "CHANGELOG"
|
44
|
+
end
|
45
|
+
rescue LoadError
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Create documentation in html directory"
|
49
|
+
task :docs=>[:yardoc]
|
50
|
+
desc "Remove temporary files and directories"
|
51
|
+
task(:clobber) { rm_rf "html" }
|
data/bin/pipemaster
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
|
2
|
+
# -*- encoding: binary -*-
|
3
|
+
require "pipemaster"
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
ENV["PIPE_ENV"] ||= "development"
|
7
|
+
server = false
|
8
|
+
daemonize = false
|
9
|
+
listeners = []
|
10
|
+
options = { :listeners => listeners }
|
11
|
+
|
12
|
+
opts = OptionParser.new("", 24, ' ') do |opts|
|
13
|
+
opts.banner = "Usage: #{File.basename($0)}\n" \
|
14
|
+
"[ruby options] [pipemaster options] command [args]\n" \
|
15
|
+
"[ruby options] [pipemaster options] --server [Pipefile]"
|
16
|
+
|
17
|
+
opts.separator "Ruby options:"
|
18
|
+
|
19
|
+
lineno = 1
|
20
|
+
opts.on("-e", "--eval LINE", "evaluate a LINE of code") do |line|
|
21
|
+
eval line, TOPLEVEL_BINDING, "-e", lineno
|
22
|
+
lineno += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") do
|
26
|
+
$DEBUG = true
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-w", "--warn", "turn warnings on for your script") do
|
30
|
+
$-w = true
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-I", "--include PATH",
|
34
|
+
"specify $LOAD_PATH (may be used more than once)") do |path|
|
35
|
+
$LOAD_PATH.unshift(*path.split(/:/))
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("-r", "--require LIBRARY",
|
39
|
+
"require the library, before executing your script") do |library|
|
40
|
+
require library
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.separator "Pipemaster options:"
|
44
|
+
|
45
|
+
opts.on("-s", "--socket {HOST:PORT|PATH}",
|
46
|
+
"communicate on HOST:PORT or PATH",
|
47
|
+
"for server, this may be specified multiple times",
|
48
|
+
"(default: #{Pipemaster::DEFAULT_LISTEN})") do |address|
|
49
|
+
listeners << address
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("-E", "--env ENVIRONMENT",
|
53
|
+
"use ENVIRONMENT for defaults (default: development)") do |e|
|
54
|
+
ENV["PIPE_ENV"] = e
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("-S", "--server", "run as server (default: client)") do |s|
|
58
|
+
server = s ? true : false
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
|
62
|
+
daemonize = d ? true : false
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.separator "Common options:"
|
66
|
+
|
67
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
68
|
+
puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '')
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on_tail("-v", "--version", "Show version") do
|
73
|
+
puts "Pipemaster v#{Pipemaster::VERSION}"
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.parse! ARGV
|
78
|
+
end
|
79
|
+
|
80
|
+
if server
|
81
|
+
|
82
|
+
gem "unicorn"
|
83
|
+
require "pipemaster/server"
|
84
|
+
require "unicorn/launcher"
|
85
|
+
config = ARGV[0] || "Pipefile"
|
86
|
+
abort "configuration file #{config} not found" unless File.exist?(config)
|
87
|
+
options[:config_file] = config
|
88
|
+
options[:working_directory] = File.dirname(config)
|
89
|
+
Unicorn::Launcher.daemonize!(options) if daemonize
|
90
|
+
Pipemaster.run(options)
|
91
|
+
|
92
|
+
else
|
93
|
+
|
94
|
+
require "pipemaster/client"
|
95
|
+
client = Pipemaster::Client.new(listeners.first)
|
96
|
+
client.input = $stdin unless $stdin.isatty
|
97
|
+
client.output = $stdout
|
98
|
+
exit client.request(*ARGV)
|
99
|
+
|
100
|
+
end
|
data/lib/pipemaster.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
module Pipemaster
|
4
|
+
# Pipemaster client. Use this to send commands to the Pipemaster server.
|
5
|
+
#
|
6
|
+
# For example:
|
7
|
+
# c = Pipemaster::Client.new
|
8
|
+
# c.request "motd"
|
9
|
+
# puts c.output.string
|
10
|
+
# => "Toilet out of order please use floor below."
|
11
|
+
#
|
12
|
+
# c = Pipemaster::Client.new(9988)
|
13
|
+
# c.input = File.open("image.png")
|
14
|
+
# c.output = File.open("thumbnail.png", "w")
|
15
|
+
# c.request "transform", "thumbnail"
|
16
|
+
class Client
|
17
|
+
BUFFER_SIZE = 16 * 1024
|
18
|
+
|
19
|
+
# Address can be "x.x.x.x:port", ":port" or UNIX socket file name.
|
20
|
+
def initialize(address)
|
21
|
+
@address = expand_addr(address || DEFAULT_LISTEN)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Set this to supply an input stream (for commands that read from $stdin).
|
25
|
+
attr_accessor :input
|
26
|
+
|
27
|
+
# Set this to supply an output stream, or read the output (defaults to
|
28
|
+
# StringIO).
|
29
|
+
attr_accessor :output
|
30
|
+
|
31
|
+
# Make a request. First argument is the command name. All other arguments
|
32
|
+
# are optional. Returns the exit code (usually 0). Will raise IOError if
|
33
|
+
# it can't talk to the server, or the server closed the connection
|
34
|
+
# prematurely.
|
35
|
+
def request(command, *args)
|
36
|
+
# Connect and send arguments.
|
37
|
+
socket = connect(@address)
|
38
|
+
socket.sync = true
|
39
|
+
header = ([command] + args).join("\0")
|
40
|
+
socket << [header.size].pack("N") << header
|
41
|
+
socket.flush
|
42
|
+
|
43
|
+
@output ||= StringIO.new
|
44
|
+
|
45
|
+
# If there's input, stream it over to the socket, while streaming the
|
46
|
+
# response back.
|
47
|
+
inputbuf, stdoutbuf = "", ""
|
48
|
+
if @input
|
49
|
+
while selected = select([@input, socket])
|
50
|
+
begin
|
51
|
+
if selected.first.include?(@input)
|
52
|
+
@input.readpartial(BUFFER_SIZE, inputbuf)
|
53
|
+
socket.write inputbuf
|
54
|
+
elsif selected.first.include?(socket)
|
55
|
+
raise IOError, "Server closed socket" if socket.eof?
|
56
|
+
@output.write stdoutbuf if stdoutbuf
|
57
|
+
stdoutbuf = socket.readpartial(BUFFER_SIZE)
|
58
|
+
end
|
59
|
+
rescue EOFError
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
socket.close_write # tell other side there's no more input
|
65
|
+
|
66
|
+
# Read remaining response and stream to output. Remember that very last
|
67
|
+
# byte is return code.
|
68
|
+
while selected = select([socket])
|
69
|
+
break if socket.eof?
|
70
|
+
@output.write stdoutbuf if stdoutbuf
|
71
|
+
stdoutbuf = socket.readpartial(BUFFER_SIZE)
|
72
|
+
end
|
73
|
+
if stdoutbuf && stdoutbuf.size > 0
|
74
|
+
status = stdoutbuf[-1]
|
75
|
+
@output.write stdoutbuf[0..-2]
|
76
|
+
return status.ord
|
77
|
+
else
|
78
|
+
raise IOError, "Server closed socket" if socket.eof?
|
79
|
+
end
|
80
|
+
ensure
|
81
|
+
socket.close rescue nil
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def connect(address)
|
87
|
+
if address[0] == ?/
|
88
|
+
UNIXSocket.open(address)
|
89
|
+
elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
|
90
|
+
TCPSocket.open($1, $2.to_i)
|
91
|
+
else
|
92
|
+
raise ArgumentError, "Don't know how to bind: #{address}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# expands "unix:path/to/foo" to a socket relative to the current path
|
97
|
+
# expands pathnames of sockets if relative to "~" or "~username"
|
98
|
+
# expands "*:port and ":port" to "127.0.0.1:port"
|
99
|
+
def expand_addr(address) #:nodoc
|
100
|
+
return "0.0.0.0:#{address}" if Integer === address
|
101
|
+
return address unless String === address
|
102
|
+
|
103
|
+
case address
|
104
|
+
when %r{\Aunix:(.*)\z}
|
105
|
+
File.expand_path($1)
|
106
|
+
when %r{\A~}
|
107
|
+
File.expand_path(address)
|
108
|
+
when %r{\A(?:\*:)?(\d+)\z}
|
109
|
+
"127.0.0.1:#$1"
|
110
|
+
when %r{\A(.*):(\d+)\z}
|
111
|
+
# canonicalize the name
|
112
|
+
packed = Socket.pack_sockaddr_in($2.to_i, $1)
|
113
|
+
Socket.unpack_sockaddr_in(packed).reverse!.join(':')
|
114
|
+
else
|
115
|
+
address
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Pipemaster
|
5
|
+
|
6
|
+
# Implements a simple DSL for configuring a Pipemaster server.
|
7
|
+
class Configurator < Unicorn::Configurator
|
8
|
+
|
9
|
+
# Default settings for Pipemaster
|
10
|
+
DEFAULTS = {
|
11
|
+
:logger => Logger.new($stderr),
|
12
|
+
:after_fork => lambda { |server, worker|
|
13
|
+
server.logger.info("spawned pid=#{$$}")
|
14
|
+
},
|
15
|
+
:before_fork => lambda { |server, worker|
|
16
|
+
server.logger.info("spawning...")
|
17
|
+
},
|
18
|
+
:before_exec => lambda { |server|
|
19
|
+
server.logger.info("forked child re-executing...")
|
20
|
+
},
|
21
|
+
:pid => nil,
|
22
|
+
:commands => {},
|
23
|
+
:timeout => 60
|
24
|
+
}
|
25
|
+
|
26
|
+
def initialize(defaults = {}) #:nodoc:
|
27
|
+
self.set = Hash.new(:unset)
|
28
|
+
use_defaults = defaults.delete(:use_defaults)
|
29
|
+
self.config_file = defaults.delete(:config_file)
|
30
|
+
set.merge!(DEFAULTS) if use_defaults
|
31
|
+
defaults.each { |key, value| self.send(key, value) }
|
32
|
+
Hash === set[:listener_opts] or
|
33
|
+
set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} }
|
34
|
+
Array === set[:listeners] or set[:listeners] = []
|
35
|
+
reload
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets object to the +new+ Logger-like object. The new logger-like
|
39
|
+
# object must respond to the following methods:
|
40
|
+
# +debug+, +info+, +warn+, +error+, +fatal+, +close+
|
41
|
+
def logger(new)
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def commands(hash)
|
46
|
+
set[:commands] = hash
|
47
|
+
end
|
48
|
+
|
49
|
+
# Defines a command.
|
50
|
+
def command(name, &block)
|
51
|
+
set[:commands][name.to_sym] = block
|
52
|
+
end
|
53
|
+
|
54
|
+
autoload :Etc, 'etc'
|
55
|
+
# Change user/group ownership of the master process.
|
56
|
+
def user(user, group = nil)
|
57
|
+
# we do not protect the caller, checking Process.euid == 0 is
|
58
|
+
# insufficient because modern systems have fine-grained
|
59
|
+
# capabilities. Let the caller handle any and all errors.
|
60
|
+
uid = Etc.getpwnam(user).uid
|
61
|
+
gid = Etc.getgrnam(group).gid if group
|
62
|
+
Unicorn::Util.chown_logs(uid, gid)
|
63
|
+
if gid && Process.egid != gid
|
64
|
+
Process.initgroups(user, gid)
|
65
|
+
Process::GID.change_privilege(gid)
|
66
|
+
end
|
67
|
+
Process.euid != uid and Process::UID.change_privilege(uid)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Sets the working directory for Pipemaster. Defaults to the location
|
71
|
+
# of the Pipefile. This may be a symlink.
|
72
|
+
def working_directory(path)
|
73
|
+
# just let chdir raise errors
|
74
|
+
path = File.expand_path(path)
|
75
|
+
if config_file &&
|
76
|
+
config_file[0] != ?/ &&
|
77
|
+
! test(?r, "#{path}/#{config_file}")
|
78
|
+
raise ArgumentError,
|
79
|
+
"pipefile=#{config_file} would not be accessible in" \
|
80
|
+
" working_directory=#{path}"
|
81
|
+
end
|
82
|
+
Dir.chdir(path)
|
83
|
+
Server::START_CTX[:cwd] = ENV["PWD"] = path
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sets after_fork hook to a given block. This block will be called by
|
87
|
+
# the worker after forking.
|
88
|
+
def after_fork(*args, &block)
|
89
|
+
set_hook(:after_fork, block_given? ? block : args[0])
|
90
|
+
end
|
91
|
+
|
92
|
+
# Sets before_fork hook to a given block. This block will be called by
|
93
|
+
# the worker before forking.
|
94
|
+
def before_fork(*args, &block)
|
95
|
+
set_hook(:before_fork, block_given? ? block : args[0])
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the before_exec hook to a given Proc object. This
|
99
|
+
# Proc object will be called by the master process right
|
100
|
+
# before exec()-ing the new binary. This is useful
|
101
|
+
# for freeing certain OS resources that you do NOT wish to
|
102
|
+
# share with the reexeced child process.
|
103
|
+
# There is no corresponding after_exec hook (for obvious reasons).
|
104
|
+
def before_exec(*args, &block)
|
105
|
+
set_hook(:before_exec, block_given? ? block : args[0], 1)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Sets listeners to the given +addresses+, replacing or augmenting the
|
109
|
+
# current set. This is for internal API use only, do not use it in your
|
110
|
+
# Pipemaster config file. Use listen instead.
|
111
|
+
def listeners(addresses) # :nodoc:
|
112
|
+
Array === addresses or addresses = Array(addresses)
|
113
|
+
addresses.map! { |addr| expand_addr(addr) }
|
114
|
+
set[:listeners] = addresses
|
115
|
+
end
|
116
|
+
|
117
|
+
# Does nothing interesting for now.
|
118
|
+
def timeout(seconds)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Does nothing interesting for now.
|
122
|
+
def worker_processes(nr)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Adds an +address+ to the existing listener set.
|
126
|
+
#
|
127
|
+
# The following options may be specified (but are generally not needed):
|
128
|
+
#
|
129
|
+
# +:backlog+: this is the backlog of the listen() syscall.
|
130
|
+
#
|
131
|
+
# Some operating systems allow negative values here to specify the
|
132
|
+
# maximum allowable value. In most cases, this number is only
|
133
|
+
# recommendation and there are other OS-specific tunables and
|
134
|
+
# variables that can affect this number. See the listen(2)
|
135
|
+
# syscall documentation of your OS for the exact semantics of
|
136
|
+
# this.
|
137
|
+
#
|
138
|
+
# If you are running pipemaster on multiple machines, lowering this number
|
139
|
+
# can help your load balancer detect when a machine is overloaded
|
140
|
+
# and give requests to a different machine.
|
141
|
+
#
|
142
|
+
# Default: 1024
|
143
|
+
#
|
144
|
+
# +:rcvbuf+, +:sndbuf+: maximum receive and send buffer sizes of sockets
|
145
|
+
#
|
146
|
+
# These correspond to the SO_RCVBUF and SO_SNDBUF settings which
|
147
|
+
# can be set via the setsockopt(2) syscall. Some kernels
|
148
|
+
# (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and
|
149
|
+
# there is no need (and it is sometimes detrimental) to specify them.
|
150
|
+
#
|
151
|
+
# See the socket API documentation of your operating system
|
152
|
+
# to determine the exact semantics of these settings and
|
153
|
+
# other operating system-specific knobs where they can be
|
154
|
+
# specified.
|
155
|
+
#
|
156
|
+
# Defaults: operating system defaults
|
157
|
+
#
|
158
|
+
# +:tcp_nodelay+: disables Nagle's algorithm on TCP sockets
|
159
|
+
#
|
160
|
+
# This has no effect on UNIX sockets.
|
161
|
+
#
|
162
|
+
# Default: operating system defaults (usually Nagle's algorithm enabled)
|
163
|
+
#
|
164
|
+
# +:tcp_nopush+: enables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
|
165
|
+
#
|
166
|
+
# This will prevent partial TCP frames from being sent out.
|
167
|
+
# Enabling +tcp_nopush+ is generally not needed or recommended as
|
168
|
+
# controlling +tcp_nodelay+ already provides sufficient latency
|
169
|
+
# reduction whereas Pipemaster does not know when the best times are
|
170
|
+
# for flushing corked sockets.
|
171
|
+
#
|
172
|
+
# This has no effect on UNIX sockets.
|
173
|
+
#
|
174
|
+
# +:tries+: times to retry binding a socket if it is already in use
|
175
|
+
#
|
176
|
+
# A negative number indicates we will retry indefinitely, this is
|
177
|
+
# useful for migrations and upgrades when individual workers
|
178
|
+
# are binding to different ports.
|
179
|
+
#
|
180
|
+
# Default: 5
|
181
|
+
#
|
182
|
+
# +:delay+: seconds to wait between successive +tries+
|
183
|
+
#
|
184
|
+
# Default: 0.5 seconds
|
185
|
+
#
|
186
|
+
# +:umask+: sets the file mode creation mask for UNIX sockets
|
187
|
+
#
|
188
|
+
# Typically UNIX domain sockets are created with more liberal
|
189
|
+
# file permissions than the rest of the application. By default,
|
190
|
+
# we create UNIX domain sockets to be readable and writable by
|
191
|
+
# all local users to give them the same accessibility as
|
192
|
+
# locally-bound TCP listeners.
|
193
|
+
#
|
194
|
+
# This has no effect on TCP listeners.
|
195
|
+
#
|
196
|
+
# Default: 0 (world read/writable)
|
197
|
+
def listen(address, opt = {})
|
198
|
+
super
|
199
|
+
end
|
200
|
+
|
201
|
+
# Sets the +path+ for the PID file of the Pipemaster master process
|
202
|
+
def pid(path)
|
203
|
+
set_path(:pid, path)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Allow redirecting $stderr to a given path. Unlike doing this from
|
207
|
+
# the shell, this allows the Pipemaster process to know the path its
|
208
|
+
# writing to and rotate the file if it is used for logging. The
|
209
|
+
# file will be opened with the File::APPEND flag and writes
|
210
|
+
# synchronized to the kernel (but not necessarily to _disk_) so
|
211
|
+
# multiple processes can safely append to it.
|
212
|
+
def stderr_path(path)
|
213
|
+
set_path(:stderr_path, path)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Same as stderr_path, except for $stdout
|
217
|
+
def stdout_path(path)
|
218
|
+
set_path(:stdout_path, path)
|
219
|
+
end
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
def preload_app(bool)
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|