ruby-watchcat-pure 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +2 -0
- data/lib/watchcat.rb +151 -41
- metadata +2 -2
data/README
CHANGED
@@ -18,6 +18,8 @@ Ruby/Watchcatd allows a Ruby application to register itself with watchcatd.
|
|
18
18
|
Ruby/Watchcatd was tested with Ruby versions >= 1.8.4 and requires watchcatd
|
19
19
|
version 1.1 and libwcat version 1.0 to be installed (see References below).
|
20
20
|
|
21
|
+
For FreeBSD support, you need at least watchcatd 1.2 (and libwcat 1.1 if you
|
22
|
+
are using the C extension).
|
21
23
|
|
22
24
|
== Installation
|
23
25
|
|
data/lib/watchcat.rb
CHANGED
@@ -13,7 +13,7 @@
|
|
13
13
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
14
14
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
15
15
|
#
|
16
|
-
# $Id: watchcat.rb
|
16
|
+
# $Id: watchcat.rb 29 2008-08-29 18:52:47Z andre $
|
17
17
|
#++
|
18
18
|
|
19
19
|
# Pure-ruby version of libwcat.
|
@@ -30,7 +30,7 @@ require 'socket'
|
|
30
30
|
#
|
31
31
|
class Watchcat
|
32
32
|
DEFAULT_TIMEOUT = 60
|
33
|
-
DEFAULT_DEVICE = '/
|
33
|
+
DEFAULT_DEVICE = '/var/run/watchcat.socket'
|
34
34
|
DEFAULT_SIGNAL = Signal.list['KILL']
|
35
35
|
|
36
36
|
# Create a new Watchcat object. The parameter hash may have the following
|
@@ -46,8 +46,8 @@ class Watchcat
|
|
46
46
|
# Should be a string which is added to the log generated by watchcatd
|
47
47
|
# when it signals a process. (default: nil)
|
48
48
|
# +device+::
|
49
|
-
# The watchcat device. (default: +/
|
50
|
-
# purposes.
|
49
|
+
# The watchcat device. (default: +/var/run/watchcat.socket+). Use for
|
50
|
+
# debugging purposes.
|
51
51
|
#
|
52
52
|
# If a block is given, the Watchcat object will be yielded and automatically
|
53
53
|
# closed on block termination.
|
@@ -60,46 +60,25 @@ class Watchcat
|
|
60
60
|
raise ArgumentError, 'timeout must be an integer'
|
61
61
|
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
when String
|
67
|
-
signal = Signal.list[args[:signal].sub(/^SIG/, '')]
|
68
|
-
raise ArgumentError, "invalid signal name" if signal.nil?
|
69
|
-
when Fixnum
|
70
|
-
signal = args[:signal]
|
71
|
-
else
|
72
|
-
raise ArgumentError, "signal must be an integer or a string"
|
73
|
-
end
|
74
|
-
|
75
|
-
@sock = UNIXSocket.new(device)
|
76
|
-
if Fcntl.const_defined? :F_SETFD
|
77
|
-
@sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
78
|
-
end
|
79
|
-
|
80
|
-
msg = "version: 1\ntimeout: #{timeout}\nsignal: #{signal}"
|
81
|
-
if info.nil?
|
82
|
-
msg << "\n\n"
|
83
|
-
else
|
84
|
-
info.gsub!(/\n/, '_')
|
85
|
-
msg << "\ninfo: #{info}\n\n"
|
86
|
-
end
|
63
|
+
signal = signal_number(args[:signal])
|
64
|
+
@sock = create_socket(device)
|
65
|
+
msg = build_message(timeout, signal, info)
|
87
66
|
|
88
67
|
safe_write(@sock, msg)
|
89
|
-
|
90
|
-
if block_given?
|
91
|
-
begin
|
92
|
-
yield(self)
|
93
|
-
ensure
|
94
|
-
@sock.close
|
95
|
-
end
|
96
|
-
end
|
97
|
-
return self
|
98
|
-
else
|
68
|
+
unless safe_read(@sock, 256) == "ok\n"
|
99
69
|
@sock.close
|
100
|
-
# Probably not the best error, but it matches
|
70
|
+
# Probably not the best error, but it matches libwcat.
|
101
71
|
raise Errno::EPERM
|
102
72
|
end
|
73
|
+
|
74
|
+
if block_given?
|
75
|
+
begin
|
76
|
+
yield(self)
|
77
|
+
ensure
|
78
|
+
@sock.close
|
79
|
+
end
|
80
|
+
end
|
81
|
+
return self
|
103
82
|
end
|
104
83
|
|
105
84
|
# Send a heartbeat to watchcatd, telling it we're still alive.
|
@@ -120,14 +99,52 @@ class Watchcat
|
|
120
99
|
|
121
100
|
private
|
122
101
|
|
102
|
+
def signal_number(value)
|
103
|
+
case value
|
104
|
+
when nil
|
105
|
+
signal = DEFAULT_SIGNAL
|
106
|
+
when String
|
107
|
+
signal = Signal.list[args[:signal].sub(/^SIG/, '')]
|
108
|
+
raise ArgumentError, "invalid signal name" if signal.nil?
|
109
|
+
when Fixnum
|
110
|
+
signal = args[:signal]
|
111
|
+
else
|
112
|
+
raise ArgumentError, "signal must be an integer or a string"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_socket(device)
|
117
|
+
sock = UNIXSocket.new(device)
|
118
|
+
if Fcntl.const_defined? :F_SETFD
|
119
|
+
sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
120
|
+
end
|
121
|
+
return sock
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_message(timeout, signal, info)
|
125
|
+
msg = "version: 1\ntimeout: #{timeout}\nsignal: #{signal}"
|
126
|
+
if info.nil?
|
127
|
+
msg << "\n\n"
|
128
|
+
else
|
129
|
+
info.gsub!(/\n/, '_')
|
130
|
+
msg << "\ninfo: #{info}\n\n"
|
131
|
+
end
|
132
|
+
return msg
|
133
|
+
end
|
134
|
+
|
123
135
|
def safe_write(fd, buf)
|
124
136
|
act = Signal.trap('PIPE', 'IGN')
|
125
137
|
begin
|
126
|
-
|
138
|
+
if RUBY_PLATFORM =~ /freebsd/i
|
139
|
+
FreeBSD.sendmsg(fd, " #{buf}") # XXX prepend an extra byte
|
140
|
+
else
|
141
|
+
fd.syswrite(buf)
|
142
|
+
end
|
127
143
|
rescue Errno::EINTR
|
128
144
|
retry
|
145
|
+
ensure
|
146
|
+
Signal.trap('PIPE', act)
|
129
147
|
end
|
130
|
-
Signal.trap('PIPE', act)
|
131
148
|
end
|
132
149
|
|
133
150
|
def safe_read(fd, len)
|
@@ -140,3 +157,96 @@ private
|
|
140
157
|
return buf
|
141
158
|
end
|
142
159
|
end
|
160
|
+
|
161
|
+
module FreeBSD # :nodoc:
|
162
|
+
extend(self)
|
163
|
+
|
164
|
+
INT_SIZE = [0].pack("i_").size
|
165
|
+
INT32_SIZE = 4
|
166
|
+
SHORT_SIZE = [0].pack("s_").size
|
167
|
+
CMGROUP_MAX = 16
|
168
|
+
ALIGNBYTES = [0].pack("L_").size - 1
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def align(p, alignbytes = ALIGNBYTES)
|
173
|
+
(p + alignbytes) & ~alignbytes
|
174
|
+
end
|
175
|
+
|
176
|
+
def sizeof(p)
|
177
|
+
align(p, 3)
|
178
|
+
end
|
179
|
+
|
180
|
+
#
|
181
|
+
# This code depends on structs cmsghdr and cmsgcred being as shown below.
|
182
|
+
# It also depends on sendmsg(2) being syscall number 28 and on the
|
183
|
+
# SOL_SOCKET and SCM_CREDS macros having the same values as the constants
|
184
|
+
# defined below.
|
185
|
+
#
|
186
|
+
# struct cmsghdr {
|
187
|
+
# socklen_t cmsg_len; /* __uint32_t */
|
188
|
+
# int cmsg_level; /* int */
|
189
|
+
# int cmsg_type; /* int */
|
190
|
+
# };
|
191
|
+
#
|
192
|
+
# struct cmsgcred {
|
193
|
+
# pid_t cmcred_pid; /* __int32_t */
|
194
|
+
# uid_t cmcred_uid; /* __uint32_t */
|
195
|
+
# uid_t cmcred_euid; /* __uint32_t */
|
196
|
+
# gid_t cmcred_gid; /* __uint32_t */
|
197
|
+
# short cmcred_ngroups; /* short */
|
198
|
+
# gid_t cmcred_groups[CMGROUP_MAX]; /* __uint32_t * 16 */
|
199
|
+
# };
|
200
|
+
#
|
201
|
+
|
202
|
+
SYS_SENDMSG = 28
|
203
|
+
SOL_SOCKET = 0xffff
|
204
|
+
SCM_CREDS = 0x03
|
205
|
+
|
206
|
+
CMSGCRED_SIZE = sizeof(4*INT_SIZE + SHORT_SIZE + CMGROUP_MAX * INT32_SIZE)
|
207
|
+
CMSGHDR_SIZE = sizeof(INT32_SIZE + 2 * INT_SIZE)
|
208
|
+
|
209
|
+
public
|
210
|
+
|
211
|
+
def sendmsg(fd, buf)
|
212
|
+
iov = [buf, buf.length].pack("pL_")
|
213
|
+
|
214
|
+
cmsg_space = cmsg_space(CMSGCRED_SIZE)
|
215
|
+
cmsg_data_len = cmsg_space - INT32_SIZE - 2*INT_SIZE
|
216
|
+
|
217
|
+
cmsghdr = ([
|
218
|
+
cmsg_len(CMSGCRED_SIZE), # cmsg_len
|
219
|
+
SOL_SOCKET, # cmsg_level
|
220
|
+
SCM_CREDS # cmsg_type
|
221
|
+
] + [0] * cmsg_data_len).pack("I_i_i_C#{cmsg_data_len}")
|
222
|
+
|
223
|
+
msg_control_ptr = pointer(cmsghdr)
|
224
|
+
msg_controllen = cmsg_space
|
225
|
+
|
226
|
+
msghdr = [
|
227
|
+
0, # msg_name
|
228
|
+
0, # msg_namelen
|
229
|
+
pointer(iov), # msg_iov
|
230
|
+
1, # msg_iovlen
|
231
|
+
pointer(cmsghdr), # msg_control
|
232
|
+
cmsg_space, # msg_controllen
|
233
|
+
0 # msg_flags
|
234
|
+
].pack("L_L_L_L_L_L_L_")
|
235
|
+
|
236
|
+
syscall(SYS_SENDMSG, fd.fileno, pointer(msghdr), 0)
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
|
241
|
+
def pointer(buf)
|
242
|
+
[buf].pack("P").unpack("L_").first
|
243
|
+
end
|
244
|
+
|
245
|
+
def cmsg_len(l)
|
246
|
+
align(CMSGHDR_SIZE) + l
|
247
|
+
end
|
248
|
+
|
249
|
+
def cmsg_space(l)
|
250
|
+
align(CMSGHDR_SIZE) + align(l)
|
251
|
+
end
|
252
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-watchcat-pure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andre Nathan
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-08-
|
12
|
+
date: 2008-08-29 00:00:00 -03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|