unicorn 5.4.0 → 5.5.0
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.
- checksums.yaml +4 -4
- data/.manifest +2 -0
- data/.olddoc.yml +1 -1
- data/Application_Timeouts +4 -4
- data/Documentation/unicorn.1.txt +1 -1
- data/Documentation/unicorn_rails.1.txt +1 -1
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/ISSUES +5 -2
- data/LATEST +25 -8
- data/LICENSE +2 -2
- data/Links +9 -7
- data/NEWS +71 -0
- data/README +13 -6
- data/Sandbox +2 -2
- data/bin/unicorn +3 -1
- data/examples/logrotate.conf +1 -1
- data/examples/nginx.conf +3 -2
- data/ext/unicorn_http/common_field_optimization.h +24 -6
- data/ext/unicorn_http/extconf.rb +30 -0
- data/ext/unicorn_http/global_variables.h +2 -2
- data/ext/unicorn_http/httpdate.c +2 -2
- data/ext/unicorn_http/unicorn_http.c +4 -9
- data/ext/unicorn_http/unicorn_http.rl +4 -9
- data/lib/unicorn.rb +19 -8
- data/lib/unicorn/configurator.rb +12 -1
- data/lib/unicorn/http_request.rb +1 -2
- data/lib/unicorn/http_server.rb +19 -20
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/socket_helper.rb +3 -2
- data/lib/unicorn/util.rb +3 -3
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +16 -2
- data/man/man1/unicorn.1 +7 -5
- data/man/man1/unicorn_rails.1 +6 -3
- data/t/README +4 -4
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +13 -0
- data/test/exec/test_exec.rb +6 -7
- data/test/unit/test_ccc.rb +1 -1
- data/test/unit/test_http_parser.rb +16 -0
- data/test/unit/test_server.rb +5 -5
- data/test/unit/test_signals.rb +2 -2
- data/test/unit/test_socket_helper.rb +4 -4
- data/test/unit/test_util.rb +25 -0
- data/unicorn.gemspec +1 -1
- metadata +5 -4
data/ext/unicorn_http/extconf.rb
CHANGED
@@ -8,4 +8,34 @@
|
|
8
8
|
have_func("rb_hash_clear", "ruby.h") # Ruby 2.0+
|
9
9
|
have_func("gmtime_r", "time.h")
|
10
10
|
|
11
|
+
message('checking if String#-@ (str_uminus) dedupes... ')
|
12
|
+
begin
|
13
|
+
a = -(%w(t e s t).join)
|
14
|
+
b = -(%w(t e s t).join)
|
15
|
+
if a.equal?(b)
|
16
|
+
$CPPFLAGS += ' -DSTR_UMINUS_DEDUPE=1 '
|
17
|
+
message("yes\n")
|
18
|
+
else
|
19
|
+
$CPPFLAGS += ' -DSTR_UMINUS_DEDUPE=0 '
|
20
|
+
message("no, needs Ruby 2.5+\n")
|
21
|
+
end
|
22
|
+
rescue NoMethodError
|
23
|
+
$CPPFLAGS += ' -DSTR_UMINUS_DEDUPE=0 '
|
24
|
+
message("no, String#-@ not available\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
message('checking if Hash#[]= (rb_hash_aset) dedupes... ')
|
28
|
+
h = {}
|
29
|
+
x = {}
|
30
|
+
r = rand.to_s
|
31
|
+
h[%W(#{r}).join('')] = :foo
|
32
|
+
x[%W(#{r}).join('')] = :foo
|
33
|
+
if x.keys[0].equal?(h.keys[0])
|
34
|
+
$CPPFLAGS += ' -DHASH_ASET_DEDUPE=1 '
|
35
|
+
message("yes\n")
|
36
|
+
else
|
37
|
+
$CPPFLAGS += ' -DHASH_ASET_DEDUPE=0 '
|
38
|
+
message("no, needs Ruby 2.6+\n")
|
39
|
+
end
|
40
|
+
|
11
41
|
create_makefile("unicorn_http")
|
@@ -56,7 +56,7 @@ NORETURN(static void parser_raise(VALUE klass, const char *));
|
|
56
56
|
/** Defines global strings in the init method. */
|
57
57
|
#define DEF_GLOBAL(N, val) do { \
|
58
58
|
g_##N = rb_obj_freeze(rb_str_new(val, sizeof(val) - 1)); \
|
59
|
-
|
59
|
+
rb_gc_register_mark_object(g_##N); \
|
60
60
|
} while (0)
|
61
61
|
|
62
62
|
/* Defines the maximum allowed lengths for various input elements.*/
|
@@ -67,7 +67,7 @@ DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewh
|
|
67
67
|
DEF_MAX_LENGTH(REQUEST_PATH, 4096); /* common PATH_MAX on modern systems */
|
68
68
|
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
69
69
|
|
70
|
-
static void init_globals(
|
70
|
+
static void init_globals(void)
|
71
71
|
{
|
72
72
|
DEF_GLOBAL(rack_url_scheme, "rack.url_scheme");
|
73
73
|
DEF_GLOBAL(request_method, "REQUEST_METHOD");
|
data/ext/unicorn_http/httpdate.c
CHANGED
@@ -64,13 +64,13 @@ static VALUE httpdate(VALUE self)
|
|
64
64
|
return buf;
|
65
65
|
}
|
66
66
|
|
67
|
-
void init_unicorn_httpdate(
|
67
|
+
void init_unicorn_httpdate(void)
|
68
68
|
{
|
69
69
|
VALUE mod = rb_define_module("Unicorn");
|
70
70
|
mod = rb_define_module_under(mod, "HttpResponse");
|
71
71
|
|
72
72
|
buf = rb_str_new(0, buf_capa - 1);
|
73
|
-
|
73
|
+
rb_gc_register_mark_object(buf);
|
74
74
|
buf_ptr = RSTRING_PTR(buf);
|
75
75
|
httpdate(Qnil);
|
76
76
|
|
@@ -15,7 +15,7 @@
|
|
15
15
|
#include "global_variables.h"
|
16
16
|
#include "c_util.h"
|
17
17
|
|
18
|
-
void init_unicorn_httpdate(
|
18
|
+
void init_unicorn_httpdate(void);
|
19
19
|
|
20
20
|
#define UH_FL_CHUNKED 0x1
|
21
21
|
#define UH_FL_HASBODY 0x2
|
@@ -4225,11 +4225,8 @@ static VALUE HttpParser_rssget(VALUE self)
|
|
4225
4225
|
|
4226
4226
|
void Init_unicorn_http(void)
|
4227
4227
|
{
|
4228
|
-
static VALUE mark_ary;
|
4229
4228
|
VALUE mUnicorn, cHttpParser;
|
4230
4229
|
|
4231
|
-
mark_ary = rb_ary_new();
|
4232
|
-
rb_global_variable(&mark_ary);
|
4233
4230
|
mUnicorn = rb_define_module("Unicorn");
|
4234
4231
|
cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
|
4235
4232
|
eHttpParserError =
|
@@ -4239,7 +4236,7 @@ void Init_unicorn_http(void)
|
|
4239
4236
|
e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
|
4240
4237
|
eHttpParserError);
|
4241
4238
|
|
4242
|
-
init_globals(
|
4239
|
+
init_globals();
|
4243
4240
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
4244
4241
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
4245
4242
|
rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
|
@@ -4276,16 +4273,14 @@ void Init_unicorn_http(void)
|
|
4276
4273
|
|
4277
4274
|
rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
|
4278
4275
|
|
4279
|
-
init_common_fields(
|
4276
|
+
init_common_fields();
|
4280
4277
|
SET_GLOBAL(g_http_host, "HOST");
|
4281
4278
|
SET_GLOBAL(g_http_trailer, "TRAILER");
|
4282
4279
|
SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
|
4283
4280
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
4284
4281
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
4285
4282
|
id_set_backtrace = rb_intern("set_backtrace");
|
4286
|
-
init_unicorn_httpdate(
|
4287
|
-
|
4288
|
-
OBJ_FREEZE(mark_ary);
|
4283
|
+
init_unicorn_httpdate();
|
4289
4284
|
|
4290
4285
|
#ifndef HAVE_RB_HASH_CLEAR
|
4291
4286
|
id_clear = rb_intern("clear");
|
@@ -13,7 +13,7 @@
|
|
13
13
|
#include "global_variables.h"
|
14
14
|
#include "c_util.h"
|
15
15
|
|
16
|
-
void init_unicorn_httpdate(
|
16
|
+
void init_unicorn_httpdate(void);
|
17
17
|
|
18
18
|
#define UH_FL_CHUNKED 0x1
|
19
19
|
#define UH_FL_HASBODY 0x2
|
@@ -931,11 +931,8 @@ static VALUE HttpParser_rssget(VALUE self)
|
|
931
931
|
|
932
932
|
void Init_unicorn_http(void)
|
933
933
|
{
|
934
|
-
static VALUE mark_ary;
|
935
934
|
VALUE mUnicorn, cHttpParser;
|
936
935
|
|
937
|
-
mark_ary = rb_ary_new();
|
938
|
-
rb_global_variable(&mark_ary);
|
939
936
|
mUnicorn = rb_define_module("Unicorn");
|
940
937
|
cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
|
941
938
|
eHttpParserError =
|
@@ -945,7 +942,7 @@ void Init_unicorn_http(void)
|
|
945
942
|
e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
|
946
943
|
eHttpParserError);
|
947
944
|
|
948
|
-
init_globals(
|
945
|
+
init_globals();
|
949
946
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
950
947
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
951
948
|
rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
|
@@ -982,16 +979,14 @@ void Init_unicorn_http(void)
|
|
982
979
|
|
983
980
|
rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
|
984
981
|
|
985
|
-
init_common_fields(
|
982
|
+
init_common_fields();
|
986
983
|
SET_GLOBAL(g_http_host, "HOST");
|
987
984
|
SET_GLOBAL(g_http_trailer, "TRAILER");
|
988
985
|
SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
|
989
986
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
990
987
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
991
988
|
id_set_backtrace = rb_intern("set_backtrace");
|
992
|
-
init_unicorn_httpdate(
|
993
|
-
|
994
|
-
OBJ_FREEZE(mark_ary);
|
989
|
+
init_unicorn_httpdate();
|
995
990
|
|
996
991
|
#ifndef HAVE_RB_HASH_CLEAR
|
997
992
|
id_clear = rb_intern("clear");
|
data/lib/unicorn.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
require 'etc'
|
3
3
|
require 'stringio'
|
4
4
|
require 'kgio'
|
5
|
+
require 'raindrops'
|
6
|
+
require 'io/wait'
|
5
7
|
|
6
8
|
begin
|
7
9
|
require 'rack'
|
@@ -43,12 +45,8 @@ def self.builder(ru, op)
|
|
43
45
|
abort "rack and Rack::Builder must be available for processing #{ru}"
|
44
46
|
end
|
45
47
|
|
46
|
-
# Op is going to get cleared before the returned lambda is called, so
|
47
|
-
# save this value so that it's still there when we need it:
|
48
|
-
no_default_middleware = op[:no_default_middleware]
|
49
|
-
|
50
48
|
# always called after config file parsing, may be called after forking
|
51
|
-
lambda do
|
49
|
+
lambda do |_, server|
|
52
50
|
inner_app = case ru
|
53
51
|
when /\.ru$/
|
54
52
|
raw = File.read(ru)
|
@@ -64,7 +62,7 @@ def self.builder(ru, op)
|
|
64
62
|
pp({ :inner_app => inner_app })
|
65
63
|
end
|
66
64
|
|
67
|
-
return inner_app
|
65
|
+
return inner_app unless server.default_middleware
|
68
66
|
|
69
67
|
middleware = { # order matters
|
70
68
|
ContentLength: nil,
|
@@ -112,9 +110,22 @@ def self.log_error(logger, prefix, exc)
|
|
112
110
|
exc.backtrace.each { |line| logger.error(line) }
|
113
111
|
end
|
114
112
|
|
115
|
-
|
113
|
+
F_SETPIPE_SZ = 1031 if RUBY_PLATFORM =~ /linux/
|
114
|
+
|
116
115
|
def self.pipe # :nodoc:
|
117
|
-
Kgio::Pipe.new.each
|
116
|
+
Kgio::Pipe.new.each do |io|
|
117
|
+
io.close_on_exec = true # remove this when we only support Ruby >= 2.0
|
118
|
+
|
119
|
+
# shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
|
120
|
+
# limits.
|
121
|
+
if defined?(F_SETPIPE_SZ)
|
122
|
+
begin
|
123
|
+
io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
|
124
|
+
rescue Errno::EINVAL
|
125
|
+
# old kernel
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
118
129
|
end
|
119
130
|
# :startdoc:
|
120
131
|
end
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -88,6 +88,9 @@ def reload(merge_defaults = true) #:nodoc:
|
|
88
88
|
RACKUP[:set_listener] and
|
89
89
|
set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
|
90
90
|
|
91
|
+
RACKUP[:no_default_middleware] and
|
92
|
+
set[:default_middleware] = false
|
93
|
+
|
91
94
|
# unicorn_rails creates dirs here after working_directory is bound
|
92
95
|
after_reload.call if after_reload
|
93
96
|
|
@@ -235,7 +238,7 @@ def before_exec(*args, &block)
|
|
235
238
|
# server 192.168.0.9:8080 fail_timeout=0;
|
236
239
|
# }
|
237
240
|
#
|
238
|
-
# See
|
241
|
+
# See https://nginx.org/en/docs/http/ngx_http_upstream_module.html
|
239
242
|
# for more details on nginx upstream configuration.
|
240
243
|
def timeout(seconds)
|
241
244
|
set_int(:timeout, seconds, 3)
|
@@ -265,6 +268,14 @@ def worker_processes(nr)
|
|
265
268
|
set_int(:worker_processes, nr, 1)
|
266
269
|
end
|
267
270
|
|
271
|
+
# sets whether to add default middleware in the development and
|
272
|
+
# deployment RACK_ENVs.
|
273
|
+
#
|
274
|
+
# default_middleware is only available in unicorn 5.5.0+
|
275
|
+
def default_middleware(bool)
|
276
|
+
set_bool(:default_middleware, bool)
|
277
|
+
end
|
278
|
+
|
268
279
|
# sets listeners to the given +addresses+, replacing or augmenting the
|
269
280
|
# current set. This is for the global listener pool shared by all
|
270
281
|
# worker processes. For per-worker listeners, see the after_fork example
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
# no stable API here
|
4
4
|
require 'unicorn_http'
|
5
|
-
require 'raindrops'
|
6
5
|
|
7
6
|
# TODO: remove redundant names
|
8
7
|
Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
|
@@ -66,7 +65,7 @@ def read(socket)
|
|
66
65
|
clear
|
67
66
|
e = env
|
68
67
|
|
69
|
-
# From
|
68
|
+
# From https://www.ietf.org/rfc/rfc3875:
|
70
69
|
# "Script authors should be aware that the REMOTE_ADDR and
|
71
70
|
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
72
71
|
# may not identify the ultimate source of the request. They
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -14,7 +14,8 @@ class Unicorn::HttpServer
|
|
14
14
|
attr_accessor :app, :timeout, :worker_processes,
|
15
15
|
:before_fork, :after_fork, :before_exec,
|
16
16
|
:listener_opts, :preload_app,
|
17
|
-
:orig_app, :config, :ready_pipe, :user
|
17
|
+
:orig_app, :config, :ready_pipe, :user,
|
18
|
+
:default_middleware
|
18
19
|
attr_writer :after_worker_exit, :after_worker_ready, :worker_exec
|
19
20
|
|
20
21
|
attr_reader :pid, :logger
|
@@ -70,6 +71,7 @@ def initialize(app, options = {})
|
|
70
71
|
@app = app
|
71
72
|
@request = Unicorn::HttpRequest.new
|
72
73
|
@reexec_pid = 0
|
74
|
+
@default_middleware = true
|
73
75
|
options = options.dup
|
74
76
|
@ready_pipe = options.delete(:ready_pipe)
|
75
77
|
@init_listeners = options[:listeners] ? options[:listeners].dup : []
|
@@ -82,7 +84,7 @@ def initialize(app, options = {})
|
|
82
84
|
# * The master process never closes or reinitializes this once
|
83
85
|
# initialized. Signal handlers in the master process will write to
|
84
86
|
# it to wake up the master from IO.select in exactly the same manner
|
85
|
-
# djb describes in
|
87
|
+
# djb describes in https://cr.yp.to/docs/selfpipe.html
|
86
88
|
#
|
87
89
|
# * The workers immediately close the pipe they inherit. See the
|
88
90
|
# Unicorn::Worker class for the pipe workers use.
|
@@ -380,7 +382,7 @@ def check_client_connection=(bool)
|
|
380
382
|
|
381
383
|
# wait for a signal hander to wake us up and then consume the pipe
|
382
384
|
def master_sleep(sec)
|
383
|
-
@self_pipe[0].
|
385
|
+
@self_pipe[0].wait(sec) or return
|
384
386
|
# 11 bytes is the maximum string length which can be embedded within
|
385
387
|
# the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
|
386
388
|
# Most reads are only one byte here and uncommon, so it's not worth a
|
@@ -520,9 +522,6 @@ def after_fork_internal
|
|
520
522
|
Unicorn::Configurator::RACKUP.clear
|
521
523
|
@ready_pipe = @init_listeners = @before_exec = @before_fork = nil
|
522
524
|
|
523
|
-
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/36450
|
524
|
-
srand # remove in unicorn 6
|
525
|
-
|
526
525
|
# The OpenSSL PRNG is seeded with only the pid, and apps with frequently
|
527
526
|
# dying workers can recycle pids
|
528
527
|
OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
|
@@ -553,9 +552,9 @@ def spawn_missing_workers
|
|
553
552
|
@workers[pid] = worker
|
554
553
|
worker.atfork_parent
|
555
554
|
end
|
556
|
-
|
557
|
-
|
558
|
-
|
555
|
+
rescue => e
|
556
|
+
@logger.error(e) rescue nil
|
557
|
+
exit!
|
559
558
|
end
|
560
559
|
|
561
560
|
def maintain_worker_count
|
@@ -586,7 +585,7 @@ def handle_error(client, e)
|
|
586
585
|
client.kgio_trywrite(err_response(code, @request.response_start_sent))
|
587
586
|
end
|
588
587
|
client.close
|
589
|
-
|
588
|
+
rescue
|
590
589
|
end
|
591
590
|
|
592
591
|
def e100_response_write(client, env)
|
@@ -669,9 +668,9 @@ def reopen_worker_logs(worker_nr)
|
|
669
668
|
logger.info "worker=#{worker_nr} reopening logs..."
|
670
669
|
Unicorn::Util.reopen_logs
|
671
670
|
logger.info "worker=#{worker_nr} done reopening logs"
|
672
|
-
|
673
|
-
|
674
|
-
|
671
|
+
rescue => e
|
672
|
+
logger.error(e) rescue nil
|
673
|
+
exit!(77) # EX_NOPERM in sysexits.h
|
675
674
|
end
|
676
675
|
|
677
676
|
# runs inside each forked worker, this sits around and waits
|
@@ -757,11 +756,11 @@ def valid_pid?(path)
|
|
757
756
|
wpid <= 0 and return
|
758
757
|
Process.kill(0, wpid)
|
759
758
|
wpid
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
759
|
+
rescue Errno::EPERM
|
760
|
+
logger.info "pid=#{path} possibly stale, got EPERM signalling PID:#{wpid}"
|
761
|
+
nil
|
762
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
763
|
+
# don't unlink stale pid files, racy without non-portable locking...
|
765
764
|
end
|
766
765
|
|
767
766
|
def load_config!
|
@@ -787,12 +786,12 @@ def listener_names(listeners = LISTENERS)
|
|
787
786
|
end
|
788
787
|
|
789
788
|
def build_app!
|
790
|
-
if app.respond_to?(:arity) && app.arity == 0
|
789
|
+
if app.respond_to?(:arity) && (app.arity == 0 || app.arity == 2)
|
791
790
|
if defined?(Gem) && Gem.respond_to?(:refresh)
|
792
791
|
logger.info "Refreshing Gem list"
|
793
792
|
Gem.refresh
|
794
793
|
end
|
795
|
-
self.app = app.call
|
794
|
+
self.app = app.arity == 0 ? app.call : app.call(nil, self)
|
796
795
|
end
|
797
796
|
end
|
798
797
|
|
data/lib/unicorn/launcher.rb
CHANGED
@@ -83,6 +83,7 @@ def set_tcp_sockopt(sock, opt)
|
|
83
83
|
rescue => e
|
84
84
|
logger.error("#{sock_name(sock)} " \
|
85
85
|
"failed to set accept_filter=#{name} (#{e.inspect})")
|
86
|
+
logger.error("perhaps accf_http(9) needs to be loaded".freeze)
|
86
87
|
end if arg != got
|
87
88
|
end
|
88
89
|
end
|
@@ -100,8 +101,8 @@ def set_server_sockopt(sock, opt)
|
|
100
101
|
log_buffer_sizes(sock, " after: ")
|
101
102
|
end
|
102
103
|
sock.listen(opt[:backlog])
|
103
|
-
|
104
|
-
|
104
|
+
rescue => e
|
105
|
+
Unicorn.log_error(logger, "#{sock_name(sock)} #{opt.inspect}", e)
|
105
106
|
end
|
106
107
|
|
107
108
|
def log_buffer_sizes(sock, pfx = '')
|
data/lib/unicorn/util.rb
CHANGED
@@ -11,8 +11,8 @@ def self.is_log?(fp)
|
|
11
11
|
fp.stat.file? &&
|
12
12
|
fp.sync &&
|
13
13
|
(fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
|
14
|
-
|
15
|
-
|
14
|
+
rescue IOError, Errno::EBADF
|
15
|
+
false
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.chown_logs(uid, gid)
|
@@ -64,7 +64,7 @@ def self.reopen_logs
|
|
64
64
|
fp.reopen(fp.path, "a")
|
65
65
|
else
|
66
66
|
# We should not need this workaround, Ruby can be fixed:
|
67
|
-
#
|
67
|
+
# https://bugs.ruby-lang.org/issues/9036
|
68
68
|
# MRI will not call call fclose(3) or freopen(3) here
|
69
69
|
# since there's no associated std{in,out,err} FILE * pointer
|
70
70
|
# This should atomically use dup3(2) (or dup2(2)) syscall
|
data/lib/unicorn/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Unicorn::Const::UNICORN_VERSION = '5.
|
1
|
+
Unicorn::Const::UNICORN_VERSION = '5.5.0'
|
data/lib/unicorn/worker.rb
CHANGED
@@ -122,6 +122,11 @@ def close # :nodoc:
|
|
122
122
|
# the +after_fork+ hook after any privileged functions need to be
|
123
123
|
# run (e.g. to set per-worker CPU affinity, niceness, etc)
|
124
124
|
#
|
125
|
+
# +group+ can be specified as a string, or as an array of two
|
126
|
+
# strings. If an array of two strings is given, the first string
|
127
|
+
# is used as the primary group of the process, and the second is
|
128
|
+
# used as the group of the log files.
|
129
|
+
#
|
125
130
|
# Any and all errors raised within this method will be propagated
|
126
131
|
# directly back to the caller (usually the +after_fork+ hook.
|
127
132
|
# These errors commonly include ArgumentError for specifying an
|
@@ -134,8 +139,17 @@ def user(user, group = nil, chroot = false)
|
|
134
139
|
# insufficient because modern systems have fine-grained
|
135
140
|
# capabilities. Let the caller handle any and all errors.
|
136
141
|
uid = Etc.getpwnam(user).uid
|
137
|
-
|
138
|
-
|
142
|
+
|
143
|
+
if group
|
144
|
+
if group.is_a?(Array)
|
145
|
+
group, log_group = group
|
146
|
+
log_gid = Etc.getgrnam(log_group).gid
|
147
|
+
end
|
148
|
+
gid = Etc.getgrnam(group).gid
|
149
|
+
log_gid ||= gid
|
150
|
+
end
|
151
|
+
|
152
|
+
Unicorn::Util.chown_logs(uid, log_gid)
|
139
153
|
if gid && Process.egid != gid
|
140
154
|
Process.initgroups(user, gid)
|
141
155
|
Process::GID.change_privilege(gid)
|