1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
| | # -*- encoding: binary -*-
require 'fcntl'
module Unicorn::Util # :nodoc:
# :stopdoc:
def self.is_log?(fp)
append_flags = File::WRONLY | File::APPEND
! fp.closed? &&
fp.stat.file? &&
fp.sync &&
(fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
rescue IOError, Errno::EBADF
false
end
def self.chown_logs(uid, gid)
ObjectSpace.each_object(File) do |fp|
fp.chown(uid, gid) if is_log?(fp)
end
end
# :startdoc:
# This reopens ALL logfiles in the process that have been rotated
# using logrotate(8) (without copytruncate) or similar tools.
# A +File+ object is considered for reopening if it is:
# 1) opened with the O_APPEND and O_WRONLY flags
# 2) the current open file handle does not match its original open path
# 3) unbuffered (as far as userspace buffering goes, not O_SYNC)
# Returns the number of files reopened
#
# In Unicorn 3.5.x and earlier, files must be opened with an absolute
# path to be considered a log file.
def self.reopen_logs
to_reopen = []
nr = 0
ObjectSpace.each_object(File) { |fp| is_log?(fp) and to_reopen << fp }
to_reopen.each do |fp|
orig_st = begin
fp.stat
rescue IOError, Errno::EBADF # race
next
end
begin
b = File.stat(fp.path)
next if orig_st.ino == b.ino && orig_st.dev == b.dev
rescue Errno::ENOENT
end
begin
# stdin, stdout, stderr are special. The following dance should
# guarantee there is no window where `fp' is unwritable in MRI
# (or any correct Ruby implementation).
#
# Fwiw, GVL has zero bearing here. This is tricky because of
# the unavoidable existence of stdio FILE * pointers for
# std{in,out,err} in all programs which may use the standard C library
if fp.fileno <= 2
# We do not want to hit fclose(3)->dup(2) window for std{in,out,err}
# MRI will use freopen(3) here internally on std{in,out,err}
fp.reopen(fp.path, "a")
else
# We should not need this workaround, Ruby can be fixed:
# https://bugs.ruby-lang.org/issues/9036
# MRI will not call call fclose(3) or freopen(3) here
# since there's no associated std{in,out,err} FILE * pointer
# This should atomically use dup3(2) (or dup2(2)) syscall
File.open(fp.path, "a") { |tmpfp| fp.reopen(tmpfp) }
end
fp.sync = true
fp.flush # IO#sync=true may not implicitly flush
new_st = fp.stat
# this should only happen in the master:
if orig_st.uid != new_st.uid || orig_st.gid != new_st.gid
fp.chown(orig_st.uid, orig_st.gid)
end
nr += 1
rescue IOError, Errno::EBADF
# not much we can do...
end
end
nr
end
end
|