ruby-watchcat-pure 1.0.0 → 1.1.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.
Files changed (3) hide show
  1. data/README +2 -0
  2. data/lib/watchcat.rb +151 -41
  3. 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 24 2008-08-04 14:47:08Z andre $
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 = '/dev/watchcat'
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: +/dev/watchcat+). Use for debugging
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
- case args[:signal]
64
- when nil
65
- signal = DEFAULT_SIGNAL
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
- if safe_read(@sock, 256) == "ok\n"
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 the C library.
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
- fd.syswrite(buf)
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.0.0
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-06 00:00:00 -03:00
12
+ date: 2008-08-29 00:00:00 -03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15