unicorn 5.4.0 → 5.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.manifest +2 -0
  3. data/.olddoc.yml +1 -1
  4. data/Application_Timeouts +4 -4
  5. data/Documentation/unicorn.1.txt +1 -1
  6. data/Documentation/unicorn_rails.1.txt +1 -1
  7. data/GIT-VERSION-FILE +1 -1
  8. data/GIT-VERSION-GEN +1 -1
  9. data/ISSUES +5 -2
  10. data/LATEST +25 -8
  11. data/LICENSE +2 -2
  12. data/Links +9 -7
  13. data/NEWS +71 -0
  14. data/README +13 -6
  15. data/Sandbox +2 -2
  16. data/bin/unicorn +3 -1
  17. data/examples/logrotate.conf +1 -1
  18. data/examples/nginx.conf +3 -2
  19. data/ext/unicorn_http/common_field_optimization.h +24 -6
  20. data/ext/unicorn_http/extconf.rb +30 -0
  21. data/ext/unicorn_http/global_variables.h +2 -2
  22. data/ext/unicorn_http/httpdate.c +2 -2
  23. data/ext/unicorn_http/unicorn_http.c +4 -9
  24. data/ext/unicorn_http/unicorn_http.rl +4 -9
  25. data/lib/unicorn.rb +19 -8
  26. data/lib/unicorn/configurator.rb +12 -1
  27. data/lib/unicorn/http_request.rb +1 -2
  28. data/lib/unicorn/http_server.rb +19 -20
  29. data/lib/unicorn/launcher.rb +1 -1
  30. data/lib/unicorn/socket_helper.rb +3 -2
  31. data/lib/unicorn/util.rb +3 -3
  32. data/lib/unicorn/version.rb +1 -1
  33. data/lib/unicorn/worker.rb +16 -2
  34. data/man/man1/unicorn.1 +7 -5
  35. data/man/man1/unicorn_rails.1 +6 -3
  36. data/t/README +4 -4
  37. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  38. data/t/t0301.ru +13 -0
  39. data/test/exec/test_exec.rb +6 -7
  40. data/test/unit/test_ccc.rb +1 -1
  41. data/test/unit/test_http_parser.rb +16 -0
  42. data/test/unit/test_server.rb +5 -5
  43. data/test/unit/test_signals.rb +2 -2
  44. data/test/unit/test_socket_helper.rb +4 -4
  45. data/test/unit/test_util.rb +25 -0
  46. data/unicorn.gemspec +1 -1
  47. metadata +5 -4
@@ -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
- rb_ary_push(mark_ary, g_##N); \
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(VALUE mark_ary)
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");
@@ -64,13 +64,13 @@ static VALUE httpdate(VALUE self)
64
64
  return buf;
65
65
  }
66
66
 
67
- void init_unicorn_httpdate(VALUE mark_ary)
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
- rb_ary_push(mark_ary, buf);
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(VALUE mark_ary);
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(mark_ary);
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(mark_ary);
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(mark_ary);
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(VALUE mark_ary);
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(mark_ary);
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(mark_ary);
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(mark_ary);
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 if no_default_middleware
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
- # remove this when we only support Ruby >= 2.0
113
+ F_SETPIPE_SZ = 1031 if RUBY_PLATFORM =~ /linux/
114
+
116
115
  def self.pipe # :nodoc:
117
- Kgio::Pipe.new.each { |io| io.close_on_exec = true }
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
@@ -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 http://nginx.org/en/docs/http/ngx_http_upstream_module.html
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
@@ -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 http://www.ietf.org/rfc/rfc3875:
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
@@ -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 http://cr.yp.to/docs/selfpipe.html
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].kgio_wait_readable(sec) or return
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
- rescue => e
557
- @logger.error(e) rescue nil
558
- exit!
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
- rescue
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
- rescue => e
673
- logger.error(e) rescue nil
674
- exit!(77) # EX_NOPERM in sysexits.h
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
- rescue Errno::EPERM
761
- logger.info "pid=#{path} possibly stale, got EPERM signalling PID:#{wpid}"
762
- nil
763
- rescue Errno::ESRCH, Errno::ENOENT
764
- # don't unlink stale pid files, racy without non-portable locking...
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
 
@@ -31,7 +31,7 @@ def self.daemonize!(options)
31
31
  # \_ parent - exits immediately ASAP
32
32
  # \_ unicorn master - writes to pipe when ready
33
33
 
34
- rd, wr = IO.pipe
34
+ rd, wr = Unicorn.pipe
35
35
  grandparent = $$
36
36
  if fork
37
37
  wr.close # grandparent does not write
@@ -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
- rescue => e
104
- Unicorn.log_error(logger, "#{sock_name(sock)} #{opt.inspect}", e)
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
- rescue IOError, Errno::EBADF
15
- false
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
- # http://bugs.ruby-lang.org/issues/9036
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
@@ -1 +1 @@
1
- Unicorn::Const::UNICORN_VERSION = '5.4.0'
1
+ Unicorn::Const::UNICORN_VERSION = '5.5.0'
@@ -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
- gid = Etc.getgrnam(group).gid if group
138
- Unicorn::Util.chown_logs(uid, gid)
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)