puma 4.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1532 -0
  3. data/LICENSE +26 -0
  4. data/README.md +291 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +31 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +37 -0
  9. data/docs/deployment.md +111 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/nginx.md +80 -0
  14. data/docs/plugins.md +38 -0
  15. data/docs/restart.md +41 -0
  16. data/docs/signals.md +96 -0
  17. data/docs/systemd.md +290 -0
  18. data/docs/tcp_mode.md +96 -0
  19. data/ext/puma_http11/PumaHttp11Service.java +19 -0
  20. data/ext/puma_http11/ext_help.h +15 -0
  21. data/ext/puma_http11/extconf.rb +28 -0
  22. data/ext/puma_http11/http11_parser.c +1044 -0
  23. data/ext/puma_http11/http11_parser.h +65 -0
  24. data/ext/puma_http11/http11_parser.java.rl +145 -0
  25. data/ext/puma_http11/http11_parser.rl +147 -0
  26. data/ext/puma_http11/http11_parser_common.rl +54 -0
  27. data/ext/puma_http11/io_buffer.c +155 -0
  28. data/ext/puma_http11/mini_ssl.c +553 -0
  29. data/ext/puma_http11/org/jruby/puma/Http11.java +226 -0
  30. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  31. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  32. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +363 -0
  33. data/ext/puma_http11/puma_http11.c +502 -0
  34. data/lib/puma.rb +31 -0
  35. data/lib/puma/accept_nonblock.rb +29 -0
  36. data/lib/puma/app/status.rb +80 -0
  37. data/lib/puma/binder.rb +385 -0
  38. data/lib/puma/cli.rb +239 -0
  39. data/lib/puma/client.rb +494 -0
  40. data/lib/puma/cluster.rb +554 -0
  41. data/lib/puma/commonlogger.rb +108 -0
  42. data/lib/puma/configuration.rb +362 -0
  43. data/lib/puma/const.rb +235 -0
  44. data/lib/puma/control_cli.rb +289 -0
  45. data/lib/puma/detect.rb +15 -0
  46. data/lib/puma/dsl.rb +740 -0
  47. data/lib/puma/events.rb +156 -0
  48. data/lib/puma/io_buffer.rb +4 -0
  49. data/lib/puma/jruby_restart.rb +84 -0
  50. data/lib/puma/launcher.rb +475 -0
  51. data/lib/puma/minissl.rb +278 -0
  52. data/lib/puma/minissl/context_builder.rb +76 -0
  53. data/lib/puma/null_io.rb +44 -0
  54. data/lib/puma/plugin.rb +120 -0
  55. data/lib/puma/plugin/tmp_restart.rb +36 -0
  56. data/lib/puma/rack/builder.rb +301 -0
  57. data/lib/puma/rack/urlmap.rb +93 -0
  58. data/lib/puma/rack_default.rb +9 -0
  59. data/lib/puma/reactor.rb +400 -0
  60. data/lib/puma/runner.rb +192 -0
  61. data/lib/puma/server.rb +1030 -0
  62. data/lib/puma/single.rb +123 -0
  63. data/lib/puma/state_file.rb +31 -0
  64. data/lib/puma/tcp_logger.rb +41 -0
  65. data/lib/puma/thread_pool.rb +328 -0
  66. data/lib/puma/util.rb +124 -0
  67. data/lib/rack/handler/puma.rb +115 -0
  68. data/tools/docker/Dockerfile +16 -0
  69. data/tools/jungle/README.md +19 -0
  70. data/tools/jungle/init.d/README.md +61 -0
  71. data/tools/jungle/init.d/puma +421 -0
  72. data/tools/jungle/init.d/run-puma +18 -0
  73. data/tools/jungle/rc.d/README.md +74 -0
  74. data/tools/jungle/rc.d/puma +61 -0
  75. data/tools/jungle/rc.d/puma.conf +10 -0
  76. data/tools/jungle/upstart/README.md +61 -0
  77. data/tools/jungle/upstart/puma-manager.conf +31 -0
  78. data/tools/jungle/upstart/puma.conf +69 -0
  79. data/tools/trickletest.rb +44 -0
  80. metadata +144 -0
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri/common'
4
+
5
+ module Puma
6
+ module Util
7
+ module_function
8
+
9
+ def pipe
10
+ IO.pipe
11
+ end
12
+
13
+ # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
14
+ # target encoding of the string returned, and it defaults to UTF-8
15
+ if defined?(::Encoding)
16
+ def unescape(s, encoding = Encoding::UTF_8)
17
+ URI.decode_www_form_component(s, encoding)
18
+ end
19
+ else
20
+ def unescape(s, encoding = nil)
21
+ URI.decode_www_form_component(s, encoding)
22
+ end
23
+ end
24
+ module_function :unescape
25
+
26
+ DEFAULT_SEP = /[&;] */n
27
+
28
+ # Stolen from Mongrel, with some small modifications:
29
+ # Parses a query string by breaking it up at the '&'
30
+ # and ';' characters. You can also use this to parse
31
+ # cookies by changing the characters used in the second
32
+ # parameter (which defaults to '&;').
33
+ def parse_query(qs, d = nil, &unescaper)
34
+ unescaper ||= method(:unescape)
35
+
36
+ params = {}
37
+
38
+ (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
39
+ next if p.empty?
40
+ k, v = p.split('=', 2).map(&unescaper)
41
+
42
+ if cur = params[k]
43
+ if cur.class == Array
44
+ params[k] << v
45
+ else
46
+ params[k] = [cur, v]
47
+ end
48
+ else
49
+ params[k] = v
50
+ end
51
+ end
52
+
53
+ return params
54
+ end
55
+
56
+ # A case-insensitive Hash that preserves the original case of a
57
+ # header when set.
58
+ class HeaderHash < Hash
59
+ def self.new(hash={})
60
+ HeaderHash === hash ? hash : super(hash)
61
+ end
62
+
63
+ def initialize(hash={})
64
+ super()
65
+ @names = {}
66
+ hash.each { |k, v| self[k] = v }
67
+ end
68
+
69
+ def each
70
+ super do |k, v|
71
+ yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
72
+ end
73
+ end
74
+
75
+ def to_hash
76
+ hash = {}
77
+ each { |k,v| hash[k] = v }
78
+ hash
79
+ end
80
+
81
+ def [](k)
82
+ super(k) || super(@names[k.downcase])
83
+ end
84
+
85
+ def []=(k, v)
86
+ canonical = k.downcase
87
+ delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
88
+ @names[k] = @names[canonical] = k
89
+ super k, v
90
+ end
91
+
92
+ def delete(k)
93
+ canonical = k.downcase
94
+ result = super @names.delete(canonical)
95
+ @names.delete_if { |name,| name.downcase == canonical }
96
+ result
97
+ end
98
+
99
+ def include?(k)
100
+ @names.include?(k) || @names.include?(k.downcase)
101
+ end
102
+
103
+ alias_method :has_key?, :include?
104
+ alias_method :member?, :include?
105
+ alias_method :key?, :include?
106
+
107
+ def merge!(other)
108
+ other.each { |k, v| self[k] = v }
109
+ self
110
+ end
111
+
112
+ def merge(other)
113
+ hash = dup
114
+ hash.merge! other
115
+ end
116
+
117
+ def replace(other)
118
+ clear
119
+ other.each { |k, v| self[k] = v }
120
+ self
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/handler'
4
+
5
+ module Rack
6
+ module Handler
7
+ module Puma
8
+ DEFAULT_OPTIONS = {
9
+ :Verbose => false,
10
+ :Silent => false
11
+ }
12
+
13
+ def self.config(app, options = {})
14
+ require 'puma'
15
+ require 'puma/configuration'
16
+ require 'puma/events'
17
+ require 'puma/launcher'
18
+
19
+ default_options = DEFAULT_OPTIONS.dup
20
+
21
+ # Libraries pass in values such as :Port and there is no way to determine
22
+ # if it is a default provided by the library or a special value provided
23
+ # by the user. A special key `user_supplied_options` can be passed. This
24
+ # contains an array of all explicitly defined user options. We then
25
+ # know that all other values are defaults
26
+ if user_supplied_options = options.delete(:user_supplied_options)
27
+ (options.keys - user_supplied_options).each do |k|
28
+ default_options[k] = options.delete(k)
29
+ end
30
+ end
31
+
32
+ conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
33
+ user_config.quiet
34
+
35
+ if options.delete(:Verbose)
36
+ app = Rack::CommonLogger.new(app, STDOUT)
37
+ end
38
+
39
+ if options[:environment]
40
+ user_config.environment options[:environment]
41
+ end
42
+
43
+ if options[:Threads]
44
+ min, max = options.delete(:Threads).split(':', 2)
45
+ user_config.threads min, max
46
+ end
47
+
48
+ if options[:Host] || options[:Port]
49
+ host = options[:Host] || default_options[:Host]
50
+ port = options[:Port] || default_options[:Port]
51
+ self.set_host_port_to_config(host, port, user_config)
52
+ end
53
+
54
+ if default_options[:Host]
55
+ file_config.set_default_host(default_options[:Host])
56
+ end
57
+ self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
58
+
59
+ user_config.app app
60
+ end
61
+ conf
62
+ end
63
+
64
+ def self.run(app, options = {})
65
+ conf = self.config(app, options)
66
+
67
+ events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
68
+
69
+ launcher = ::Puma::Launcher.new(conf, :events => events)
70
+
71
+ yield launcher if block_given?
72
+ begin
73
+ launcher.run
74
+ rescue Interrupt
75
+ puts "* Gracefully stopping, waiting for requests to finish"
76
+ launcher.stop
77
+ puts "* Goodbye!"
78
+ end
79
+ end
80
+
81
+ def self.valid_options
82
+ {
83
+ "Host=HOST" => "Hostname to listen on (default: localhost)",
84
+ "Port=PORT" => "Port to listen on (default: 8080)",
85
+ "Threads=MIN:MAX" => "min:max threads to use (default 0:16)",
86
+ "Verbose" => "Don't report each request (default: false)"
87
+ }
88
+ end
89
+
90
+ def self.set_host_port_to_config(host, port, config)
91
+ config.clear_binds! if host || port
92
+
93
+ if host && (host[0,1] == '.' || host[0,1] == '/')
94
+ config.bind "unix://#{host}"
95
+ elsif host && host =~ /^ssl:\/\//
96
+ uri = URI.parse(host)
97
+ uri.port ||= port || ::Puma::Configuration::DefaultTCPPort
98
+ config.bind uri.to_s
99
+ else
100
+
101
+ if host
102
+ port ||= ::Puma::Configuration::DefaultTCPPort
103
+ end
104
+
105
+ if port
106
+ host ||= ::Puma::Configuration::DefaultTCPHost
107
+ config.port port, host
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ register :puma, Puma
114
+ end
115
+ end
@@ -0,0 +1,16 @@
1
+ # Use this Dockerfile to create minimal reproductions of issues
2
+
3
+ FROM ruby:2.6
4
+
5
+ # throw errors if Gemfile has been modified since Gemfile.lock
6
+ RUN bundle config --global frozen 1
7
+
8
+ WORKDIR /usr/src/app
9
+
10
+ COPY . .
11
+ RUN gem install bundler
12
+ RUN bundle install
13
+ RUN bundle exec rake compile
14
+
15
+ EXPOSE 9292
16
+ CMD bundle exec bin/puma test/rackup/hello.ru
@@ -0,0 +1,19 @@
1
+ # Puma as a service
2
+
3
+ ## Upstart
4
+
5
+ See `/tools/jungle/upstart` for Ubuntu's upstart scripts.
6
+
7
+ ## Systemd
8
+
9
+ See [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md).
10
+
11
+ ## Init.d
12
+
13
+ Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS.
14
+
15
+ See `/tools/jungle/init.d` for tools to use with init.d and start-stop-daemon.
16
+
17
+ ## rc.d
18
+
19
+ See `/tools/jungle/rc.d` for FreeBSD's rc.d scripts
@@ -0,0 +1,61 @@
1
+ # Puma daemon service
2
+
3
+ Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS.
4
+
5
+ Init script to manage multiple Puma servers on the same box using start-stop-daemon.
6
+
7
+ ## Installation
8
+
9
+ # Copy the init script to services directory
10
+ sudo cp puma /etc/init.d
11
+ sudo chmod +x /etc/init.d/puma
12
+
13
+ # Make it start at boot time.
14
+ sudo update-rc.d -f puma defaults
15
+
16
+ # Copy the Puma runner to an accessible location
17
+ sudo cp run-puma /usr/local/bin
18
+ sudo chmod +x /usr/local/bin/run-puma
19
+
20
+ # Create an empty configuration file
21
+ sudo touch /etc/puma.conf
22
+
23
+ ## Managing the jungle
24
+
25
+ Puma apps are held in /etc/puma.conf by default. It's mainly a CSV file and every line represents one app. Here's the syntax:
26
+
27
+ app-path,user,config-file-path,log-file-path,environment-variables
28
+
29
+ You can add an instance by editing the file or running the following command:
30
+
31
+ sudo /etc/init.d/puma add /path/to/app user /path/to/app/config/puma.rb /path/to/app/log/puma.log
32
+
33
+ The config and log paths, as well as the environment variables, are optional parameters and default to:
34
+
35
+ * config: /path/to/app/*config/puma.rb*
36
+ * log: /path/to/app/*log/puma.log*
37
+ * environment: (empty)
38
+
39
+ Multiple environment variables need to be separated by a semicolon, e.g.
40
+
41
+ FOO=1;BAR=2
42
+
43
+ To remove an app, simply delete the line from the config file or run:
44
+
45
+ sudo /etc/init.d/puma remove /path/to/app
46
+
47
+ The command will make sure the Puma instance stops before removing it from the jungle.
48
+
49
+ ## Assumptions
50
+
51
+ * The script expects a temporary folder named /path/to/app/*tmp/puma* to exist. Create it if it's not there by default.
52
+ The pid and state files should live there and must be called: *tmp/puma/pid* and *tmp/puma/state*.
53
+ You can change those if you want but you'll have to adapt the script for it to work.
54
+
55
+ * Here's what a minimal app's config file should have:
56
+
57
+ ```
58
+ pidfile "/path/to/app/tmp/puma/pid"
59
+ state_path "/path/to/app/tmp/puma/state"
60
+ activate_control_app
61
+ ```
@@ -0,0 +1,421 @@
1
+ #! /bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: puma
4
+ # Required-Start: $remote_fs $syslog
5
+ # Required-Stop: $remote_fs $syslog
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: 0 1 6
8
+ # Short-Description: Puma web server
9
+ # Description: A ruby web server built for concurrency http://puma.io
10
+ # initscript to be placed in /etc/init.d.
11
+ ### END INIT INFO
12
+
13
+ # Author: Darío Javier Cravero <dario@exordo.com>
14
+ #
15
+ # Do NOT "set -e"
16
+
17
+ # PATH should only include /usr/* if it runs after the mountnfs.sh script
18
+ PATH=/usr/local/bin:/usr/local/sbin/:/sbin:/usr/sbin:/bin:/usr/bin
19
+ DESC="Puma rack web server"
20
+ NAME=puma
21
+ DAEMON=$NAME
22
+ SCRIPTNAME=/etc/init.d/$NAME
23
+ CONFIG=/etc/puma.conf
24
+ JUNGLE=`cat $CONFIG`
25
+ RUNPUMA=/usr/local/bin/run-puma
26
+ USE_LOCAL_BUNDLE=0
27
+
28
+ # Load the VERBOSE setting and other rcS variables
29
+ . /lib/init/vars.sh
30
+
31
+ # Define LSB log_* functions.
32
+ # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
33
+ . /lib/lsb/init-functions
34
+
35
+ #
36
+ # Function that starts the jungle
37
+ #
38
+ do_start() {
39
+ log_daemon_msg "=> Running the jungle..."
40
+ for i in $JUNGLE; do
41
+ dir=`echo $i | cut -d , -f 1`
42
+ do_start_one $dir
43
+ done
44
+ }
45
+
46
+ do_start_one() {
47
+ PIDFILE=$1/tmp/puma/pid
48
+ if [ -e $PIDFILE ]; then
49
+ PID=`cat $PIDFILE`
50
+ # If the puma is running, restart it, otherwise run it.
51
+ if ps -p $PID > /dev/null; then
52
+ do_restart_one $1
53
+ else
54
+ do_start_one_do $1
55
+ fi
56
+ else
57
+ do_start_one_do $1
58
+ fi
59
+ }
60
+
61
+ do_start_one_do() {
62
+ i=`grep $1 $CONFIG`
63
+ dir=`echo $i | cut -d , -f 1`
64
+ user=`echo $i | cut -d , -f 2`
65
+ config_file=`echo $i | cut -d , -f 3`
66
+ if [ "$config_file" = "" ]; then
67
+ config_file="$dir/config/puma.rb"
68
+ fi
69
+ log_file=`echo $i | cut -d , -f 4`
70
+ if [ "$log_file" = "" ]; then
71
+ log_file="$dir/log/puma.log"
72
+ fi
73
+ environment=`echo $i | cut -d , -f 5`
74
+
75
+ log_daemon_msg "--> Woke up puma $dir"
76
+ log_daemon_msg "user $user"
77
+ log_daemon_msg "log to $log_file"
78
+
79
+ if [ ! -z "$environment" ]; then
80
+ for e in $(echo "$environment" | tr ';' '\n'); do
81
+ log_daemon_msg "environment $e"
82
+ v=${e%%\=*} ; eval "$e" ; export $v
83
+ done
84
+ fi
85
+
86
+ start-stop-daemon --verbose --start --chdir $dir --chuid $user --background --exec $RUNPUMA -- $dir $config_file $log_file
87
+ }
88
+
89
+ #
90
+ # Function that stops the jungle
91
+ #
92
+ do_stop() {
93
+ log_daemon_msg "=> Putting all the beasts to bed..."
94
+ for i in $JUNGLE; do
95
+ dir=`echo $i | cut -d , -f 1`
96
+ do_stop_one $dir
97
+ done
98
+ }
99
+ #
100
+ # Function that stops the daemon/service
101
+ #
102
+ do_stop_one() {
103
+ log_daemon_msg "--> Stopping $1"
104
+ PIDFILE=$1/tmp/puma/pid
105
+ STATEFILE=$1/tmp/puma/state
106
+ if [ -e $PIDFILE ]; then
107
+ PID=`cat $PIDFILE`
108
+ if ps -p $PID > /dev/null; then
109
+ log_daemon_msg "---> About to kill PID `cat $PIDFILE`"
110
+ if [ "$USE_LOCAL_BUNDLE" -eq 1 ]; then
111
+ cd $1 && bundle exec pumactl --state $STATEFILE stop
112
+ else
113
+ pumactl --state $STATEFILE stop
114
+ fi
115
+ # Many daemons don't delete their pidfiles when they exit.
116
+ rm -f $PIDFILE $STATEFILE
117
+ else
118
+ log_daemon_msg "---> Puma $1 isn't running."
119
+ fi
120
+ else
121
+ log_daemon_msg "---> No puma here..."
122
+ fi
123
+ return 0
124
+ }
125
+
126
+ #
127
+ # Function that restarts the jungle
128
+ #
129
+ do_restart() {
130
+ for i in $JUNGLE; do
131
+ dir=`echo $i | cut -d , -f 1`
132
+ do_restart_one $dir
133
+ done
134
+ }
135
+
136
+ #
137
+ # Function that sends a SIGUSR2 to the daemon/service
138
+ #
139
+ do_restart_one() {
140
+ PIDFILE=$1/tmp/puma/pid
141
+
142
+ if [ -e $PIDFILE ]; then
143
+ log_daemon_msg "--> About to restart puma $1"
144
+ kill -s USR2 `cat $PIDFILE`
145
+ # TODO Check if process exist
146
+ else
147
+ log_daemon_msg "--> Your puma was never playing... Let's get it out there first"
148
+ do_start_one $1
149
+ fi
150
+ return 0
151
+ }
152
+
153
+ #
154
+ # Function that phased restarts the jungle
155
+ #
156
+ do_phased_restart() {
157
+ for i in $JUNGLE; do
158
+ dir=`echo $i | cut -d , -f 1`
159
+ do_phased_restart_one $dir
160
+ done
161
+ }
162
+
163
+ #
164
+ # Function that sends a SIGUSR1 to the daemon/service
165
+ #
166
+ do_phased_restart_one() {
167
+ PIDFILE=$1/tmp/puma/pid
168
+
169
+ if [ -e $PIDFILE ]; then
170
+ log_daemon_msg "--> About to restart puma $1"
171
+ kill -s USR1 `cat $PIDFILE`
172
+ # TODO Check if process exist
173
+ else
174
+ log_daemon_msg "--> Your puma was never playing... Let's get it out there first"
175
+ do_start_one $1
176
+ fi
177
+ return 0
178
+ }
179
+
180
+ #
181
+ # Function that statuss the jungle
182
+ #
183
+ do_status() {
184
+ for i in $JUNGLE; do
185
+ dir=`echo $i | cut -d , -f 1`
186
+ do_status_one $dir
187
+ done
188
+ }
189
+
190
+ #
191
+ # Function that sends a SIGUSR2 to the daemon/service
192
+ #
193
+ do_status_one() {
194
+ PIDFILE=$1/tmp/puma/pid
195
+ i=`grep $1 $CONFIG`
196
+ dir=`echo $i | cut -d , -f 1`
197
+
198
+ if [ -e $PIDFILE ]; then
199
+ log_daemon_msg "--> About to status puma $1"
200
+ if [ "$USE_LOCAL_BUNDLE" -eq 1 ]; then
201
+ cd $1 && bundle exec pumactl --state $dir/tmp/puma/state stats
202
+ else
203
+ pumactl --state $dir/tmp/puma/state stats
204
+ fi
205
+ # kill -s USR2 `cat $PIDFILE`
206
+ # TODO Check if process exist
207
+ else
208
+ log_daemon_msg "--> $1 isn't there :(..."
209
+ fi
210
+ return 0
211
+ }
212
+
213
+ do_add() {
214
+ str=""
215
+ # App's directory
216
+ if [ -d "$1" ]; then
217
+ if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
218
+ str=$1
219
+ else
220
+ echo "The app is already being managed. Remove it if you want to update its config."
221
+ exit 1
222
+ fi
223
+ else
224
+ echo "The directory $1 doesn't exist."
225
+ exit 1
226
+ fi
227
+ # User to run it as
228
+ if [ "`grep -c "^$2:" /etc/passwd`" -eq 0 ]; then
229
+ echo "The user $2 doesn't exist."
230
+ exit 1
231
+ else
232
+ str="$str,$2"
233
+ fi
234
+ # Config file
235
+ if [ "$3" != "" ]; then
236
+ if [ -e $3 ]; then
237
+ str="$str,$3"
238
+ else
239
+ echo "The config file $3 doesn't exist."
240
+ exit 1
241
+ fi
242
+ fi
243
+ # Log file
244
+ if [ "$4" != "" ]; then
245
+ str="$str,$4"
246
+ fi
247
+
248
+ # Environment variables
249
+ if [ "$5" != "" ]; then
250
+ str="$str,$5"
251
+ fi
252
+
253
+ # Add it to the jungle
254
+ echo $str >> $CONFIG
255
+ log_daemon_msg "Added a Puma to the jungle: $str. You still have to start it though."
256
+ }
257
+
258
+ do_remove() {
259
+ if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
260
+ echo "There's no app $1 to remove."
261
+ else
262
+ # Stop it first.
263
+ do_stop_one $1
264
+ # Remove it from the config.
265
+ sed -i "\\:^$1:d" $CONFIG
266
+ log_daemon_msg "Removed a Puma from the jungle: $1."
267
+ fi
268
+ }
269
+
270
+ config_bundler() {
271
+ HOME="$(eval echo ~$(id -un))"
272
+
273
+ if [ -d "$1/.rbenv/bin" ]; then
274
+ PATH="$1/.rbenv/bin:$1/.rbenv/shims:$1"
275
+ eval "$(rbenv init -)"
276
+ USE_LOCAL_BUNDLE=1
277
+ return 0
278
+
279
+ elif [ -d "/usr/local/rbenv/bin" ]; then
280
+ PATH="/usr/local/rbenv/bin:/usr/local/rbenv/shims:$PATH"
281
+ eval "$(rbenv init -)"
282
+ USE_LOCAL_BUNDLE=1
283
+ return 0
284
+
285
+ elif [ -d "$HOME/.rbenv/bin" ]; then
286
+ PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
287
+ eval "$(rbenv init -)"
288
+ USE_LOCAL_BUNDLE=1
289
+ return 0
290
+
291
+ # TODO: test rvm
292
+ # elif [ -f /etc/profile.d/rvm.sh ]; then
293
+ # source /etc/profile.d/rvm.sh
294
+ # elif [ -f /usr/local/rvm/scripts/rvm ]; then
295
+ # source /etc/profile.d/rvm.sh
296
+ # elif [ -f "$HOME/.rvm/scripts/rvm" ]; then
297
+ # source "$HOME/.rvm/scripts/rvm"
298
+ # TODO: don't know what to do with chruby
299
+ # elif [ -f /usr/local/share/chruby/chruby.sh ]; then
300
+ # source /usr/local/share/chruby/chruby.sh
301
+ # if [ -f /usr/local/share/chruby/auto.sh ]; then
302
+ # source /usr/local/share/chruby/auto.sh
303
+ # fi
304
+ # if you aren't using auto, set your version here
305
+ # chruby 2.0.0
306
+ fi
307
+
308
+ return 1
309
+ }
310
+
311
+ config_bundler
312
+
313
+ case "$1" in
314
+ start)
315
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
316
+ if [ "$#" -eq 1 ]; then
317
+ do_start
318
+ else
319
+ i=`grep $2 $CONFIG`
320
+ dir=`echo $i | cut -d , -f 1`
321
+ do_start_one $dir
322
+ fi
323
+ case "$?" in
324
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
325
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
326
+ esac
327
+ ;;
328
+ stop)
329
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
330
+ if [ "$#" -eq 1 ]; then
331
+ do_stop
332
+ else
333
+ i=`grep $2 $CONFIG`
334
+ dir=`echo $i | cut -d , -f 1`
335
+ do_stop_one $dir
336
+ fi
337
+ case "$?" in
338
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
339
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
340
+ esac
341
+ ;;
342
+ status)
343
+ # TODO Implement.
344
+ log_daemon_msg "Status $DESC" "$NAME"
345
+ if [ "$#" -eq 1 ]; then
346
+ do_status
347
+ else
348
+ i=`grep $2 $CONFIG`
349
+ dir=`echo $i | cut -d , -f 1`
350
+ do_status_one $dir
351
+ fi
352
+ case "$?" in
353
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
354
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
355
+ esac
356
+ ;;
357
+ restart)
358
+ log_daemon_msg "Restarting $DESC" "$NAME"
359
+ if [ "$#" -eq 1 ]; then
360
+ do_restart
361
+ else
362
+ i=`grep $2 $CONFIG`
363
+ dir=`echo $i | cut -d , -f 1`
364
+ do_restart_one $dir
365
+ fi
366
+ case "$?" in
367
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
368
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
369
+ esac
370
+ ;;
371
+ phased-restart)
372
+ log_daemon_msg "Restarting (phased) $DESC" "$NAME"
373
+ if [ "$#" -eq 1 ]; then
374
+ do_phased_restart
375
+ else
376
+ i=`grep $2 $CONFIG`
377
+ dir=`echo $i | cut -d , -f 1`
378
+ do_phased_restart_one $dir
379
+ fi
380
+ case "$?" in
381
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
382
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
383
+ esac
384
+ ;;
385
+ add)
386
+ if [ "$#" -lt 3 ]; then
387
+ echo "Please, specify the app's directory and the user that will run it at least."
388
+ echo " Usage: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
389
+ echo " config and log are optionals."
390
+ exit 1
391
+ else
392
+ do_add $2 $3 $4 $5
393
+ fi
394
+ case "$?" in
395
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
396
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
397
+ esac
398
+ ;;
399
+ remove)
400
+ if [ "$#" -lt 2 ]; then
401
+ echo "Please, specify the app's directory to remove."
402
+ exit 1
403
+ else
404
+ do_remove $2
405
+ fi
406
+ case "$?" in
407
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
408
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
409
+ esac
410
+ ;;
411
+ *)
412
+ echo "Usage:" >&2
413
+ echo " Run the jungle: $SCRIPTNAME {start|stop|status|restart|phased-restart}" >&2
414
+ echo " Add a Puma: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
415
+ echo " config and log are optionals."
416
+ echo " Remove a Puma: $SCRIPTNAME remove /path/to/app"
417
+ echo " On a Puma: $SCRIPTNAME {start|stop|status|restart|phased-restart} PUMA-NAME" >&2
418
+ exit 3
419
+ ;;
420
+ esac
421
+ :