distribustream 0.2.1 → 0.3.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.
@@ -1,11 +1,23 @@
1
1
  #--
2
2
  # Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
3
- # All rights reserved. See COPYING for permissions.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
4
16
  #
5
17
  # This source file is distributed as part of the
6
18
  # DistribuStream file transfer system.
7
19
  #
8
- # See http://distribustream.rubyforge.org/
20
+ # See http://distribustream.org/
9
21
  #++
10
22
 
11
23
  require 'rubygems'
@@ -1,18 +1,25 @@
1
1
  #--
2
2
  # Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
3
- # All rights reserved. See COPYING for permissions.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
4
16
  #
5
17
  # This source file is distributed as part of the
6
18
  # DistribuStream file transfer system.
7
19
  #
8
- # See http://distribustream.rubyforge.org/
20
+ # See http://distribustream.org/
9
21
  #++
10
22
 
11
- require 'rubygems'
12
- require 'mongrel'
13
- require 'uri'
14
- require 'pathname'
15
-
16
23
  require File.dirname(__FILE__) + '/../common/file_service.rb'
17
24
  require File.dirname(__FILE__) + '/file_buffer.rb'
18
25
 
@@ -23,25 +30,24 @@ module PDTP
23
30
  class FileInfo < PDTP::FileInfo
24
31
  def initialize(filename, io = nil)
25
32
  @buffer = FileBuffer.new io || open(filename, 'w')
26
- @lock = Mutex.new
27
33
  end
28
34
 
29
35
  # Write data into buffer starting at start_pos
30
36
  def write(start_pos,data)
31
- @lock.synchronize { @buffer.write start_pos, data }
37
+ @buffer.write start_pos, data
32
38
  end
33
39
 
34
40
  # Read a range of data out of buffer. Takes a ruby Range object
35
41
  def read(range)
36
42
  begin
37
- @lock.synchronize { @buffer.read range }
43
+ @buffer.read range
38
44
  rescue nil
39
45
  end
40
46
  end
41
47
 
42
48
  # Return the number of bytes currently stored
43
49
  def bytes_downloaded
44
- @lock.synchronize { @buffer.bytes_stored }
50
+ @buffer.bytes_stored
45
51
  end
46
52
  end
47
53
 
@@ -1,34 +1,33 @@
1
- # $Id: httpclient.rb 518 2007-08-30 10:17:02Z blackhedd $
2
- #
3
- # Author:: Francis Cianfrocca (gmail: blackhedd)
4
- # Homepage:: http://rubyeventmachine.com
5
- # Date:: 16 July 2006
6
- #
7
- # See EventMachine and EventMachine::Connection for documentation and
8
- # usage examples.
9
- #
10
- #----------------------------------------------------------------------------
11
- #
1
+ #--
12
2
  # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
3
  # Gmail: blackhedd
4
+ # Modifications (C) 2007 ClickCaster, Inc.
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
14
15
  #
15
- # This program is free software; you can redistribute it and/or modify
16
- # it under the terms of either: 1) the GNU General Public License
17
- # as published by the Free Software Foundation; either version 2 of the
18
- # License, or (at your option) any later version; or 2) Ruby's License.
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
18
  #
20
- # See the file COPYING for complete licensing information.
19
+ # This source file is distributed as part of the
20
+ # DistribuStream file transfer system.
21
21
  #
22
- #---------------------------------------------------------------------------
22
+ # See http://distribustream.org/
23
23
  #
24
- #
25
-
26
24
  # This version of HttpClient has been modified for use in DistribuStream
27
25
  # Notable changes:
28
26
  # - Moved into PDTP::Client namespace
29
27
  # - HTTP/1.1 switched to HTTP/1.0
30
28
  # - Introduced support for HTTP ranges
31
29
  # - Support for X headers
30
+ #++
32
31
 
33
32
  module PDTP
34
33
  class Client
@@ -1,11 +1,23 @@
1
1
  #--
2
2
  # Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
3
- # All rights reserved. See COPYING for permissions.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
4
16
  #
5
17
  # This source file is distributed as part of the
6
18
  # DistribuStream file transfer system.
7
19
  #
8
- # See http://distribustream.rubyforge.org/
20
+ # See http://distribustream.org/
9
21
  #++
10
22
 
11
23
  require 'rubygems'
@@ -27,12 +39,12 @@ module PDTP
27
39
  # This method is called after a connection to the server
28
40
  # has been successfully established.
29
41
  def connection_created(connection)
30
- @@log.debug("[mongrel] Opened connection...");
42
+ #@@log.debug("[mongrel] Opened connection...");
31
43
  end
32
44
 
33
45
  # This method is called when the server connection is destroyed
34
46
  def connection_destroyed(connection)
35
- @@log.debug("[mongrel] Closed connection...")
47
+ #@@log.debug("[mongrel] Closed connection...")
36
48
  end
37
49
 
38
50
  # Returns a transfer object if the given connection is a peer associated with
@@ -42,11 +54,10 @@ module PDTP
42
54
  nil
43
55
  end
44
56
 
45
- # This method is called when an HTTP request is received. It is called in
46
- # a separate thread, one for each request.
47
- def process(request,response)
57
+ # This method is called when an HTTP request is received.
58
+ def process(request,response)
48
59
  begin
49
- @@log.debug "Creating Transfer::Listener"
60
+ #@@log.debug "Creating Transfer::Listener"
50
61
  transfer = Transfer::Listener.new(
51
62
  @client.connection,
52
63
  request,
@@ -54,9 +65,8 @@ module PDTP
54
65
  client.file_service
55
66
  )
56
67
 
57
- #Needs to be locked because multiple threads could attempt to append a transfer at once
58
- @client.lock.synchronize { @client.transfers << transfer }
59
- transfer.handle_header
68
+ @client.transfers << transfer
69
+ transfer.handle_header
60
70
  rescue Exception => e
61
71
  raise e if transfer.nil?
62
72
  transfer.write_http_exception(e)
@@ -1,15 +1,25 @@
1
1
  #--
2
2
  # Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
3
- # All rights reserved. See COPYING for permissions.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
4
16
  #
5
17
  # This source file is distributed as part of the
6
18
  # DistribuStream file transfer system.
7
19
  #
8
- # See http://distribustream.rubyforge.org/
20
+ # See http://distribustream.org/
9
21
  #++
10
22
 
11
- require "thread"
12
- #require "net/http"
13
23
  require "uri"
14
24
  require "digest/sha2"
15
25
 
@@ -92,20 +102,16 @@ module PDTP
92
102
  out.write(e.to_s + "\n\n" + e.backtrace.join("\n") )
93
103
  end
94
104
  else
95
- @@log.info("MONGREL SERVER ERROR: exception:" + e.to_s+"\n\n"+e.backtrace.join("\n"))
105
+ STDERR.write "MONGREL SERVER ERROR: exception: " + e.to_s+"\n\n"+e.backtrace.join("\n")+"\n"
96
106
  @response.start(500) do |head,out|
97
- out.write("Server error, unknown exception:"+e.to_s + "\n\n" + e.backtrace.join("\n") )
107
+ out.write("Server error, unknown exception: "+e.to_s + "\n\n" + e.backtrace.join("\n") )
98
108
  end
99
109
  end
100
110
  end
101
111
 
102
112
  # Parse the HTTP header and ask for verification of transfer
103
- # Thread is stopped after asking for verification and will
104
- # be restarted when verification arrives
105
113
  def handle_header
106
- @thread=Thread.current
107
-
108
- @@log.debug "params=#{@request.params.inspect}"
114
+ #@@log.debug "params=#{@request.params.inspect}"
109
115
 
110
116
  @method=@request.params["REQUEST_METHOD"].downcase
111
117
  @peer=@request.params["REMOTE_ADDR"]
@@ -124,33 +130,24 @@ module PDTP
124
130
  raise HTTPException.new(400, "Missing Range header") if @byte_range.nil?
125
131
 
126
132
  send_ask_verify_message
127
- Thread.stop
128
- after_verification
133
+ @response.pending = true
129
134
  end
130
135
 
131
136
  # Called after receiving verification message from the server
132
- # Set the authorized status and restart the thread
133
- # This throws us into after_verification
134
137
  def tell_verify(authorized)
135
- @authorized=authorized
136
- @thread.run
137
- end
138
-
139
- # Perform the transfer if verification was successful
140
- def after_verification
141
138
  #check if the server authorized us
142
- unless @authorized
139
+ unless authorized
143
140
  raise HTTPException.new(403,"Forbidden: the server did not authorize this transfer")
144
141
  end
145
142
 
146
143
  info = @file_service.get_info(@url)
147
144
  if @method == "put"
148
145
  #we are the taker
149
- @@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}")
146
+ #@@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}")
150
147
 
151
148
  @file_service.set_info(FileInfo.new) if info.nil?
152
- info.write(@byte_range.first, @request.body.read)
153
- @hash=Digest::SHA256.hexdigest(res.body) rescue nil
149
+ info.write @byte_range.first, @request.body.read
150
+ @hash = Digest::SHA256.hexdigest(res.body) rescue nil
154
151
 
155
152
  # Stock HTTP OK response
156
153
  @response.start(200) do |head,out|
@@ -158,7 +155,7 @@ module PDTP
158
155
  elsif @method=="get"
159
156
  #we are the giver
160
157
  raise HTTPException.new(404,"File not found: #{@url}") if info.nil?
161
- data=info.read(@byte_range)
158
+ data = info.read @byte_range
162
159
  raise HTTPException.new(416,"Invalid range: #{@byte_range.inspect}") if data.nil?
163
160
 
164
161
  #Request was GET, so now we need to send the data
@@ -167,8 +164,11 @@ module PDTP
167
164
  head['Content-Range'] = "bytes #{@byte_range.first}-#{@byte_range.last}/*"
168
165
  #FIXME must include a DATE header according to http
169
166
 
170
- out.write(data)
167
+ out.write data
171
168
  end
169
+
170
+ # Call response.finished ourselves since we left the response pending
171
+ @response.finished
172
172
  else
173
173
  raise HTTPException.new(405,"Invalid method: #{@method}")
174
174
  end
@@ -220,11 +220,11 @@ module PDTP
220
220
  hash = nil
221
221
 
222
222
  if @method == 'get' and response[:status] == 206
223
- @@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
223
+ #@@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
224
224
  info.write(@byte_range.first,response[:content])
225
225
  hash = Digest::SHA256.hexdigest(response[:content]) rescue nil
226
226
  elsif @method == 'put' and response[:status] == 200
227
- @@log.debug("Body Uploaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
227
+ #@@log.debug("Body Uploaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
228
228
  else
229
229
  raise RuntimeError, "Invalid method/status combination: #{@method} #{response[:status]}"
230
230
  end
@@ -1,15 +1,30 @@
1
1
  #--
2
2
  # Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
3
- # All rights reserved. See COPYING for permissions.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
4
16
  #
5
17
  # This source file is distributed as part of the
6
18
  # DistribuStream file transfer system.
7
19
  #
8
- # See http://distribustream.rubyforge.org/
20
+ # See http://distribustream.org/
9
21
  #++
10
22
 
11
23
  # Namespace for all PDTP components
12
24
  module PDTP
13
- PDTP::VERSION = '0.2.1' unless defined? PDTP::VERSION
25
+ PDTP::VERSION = '0.3.0' unless defined? PDTP::VERSION
14
26
  def self.version() VERSION end
15
- end
27
+
28
+ PDTP::DEFAULT_PORT = 6086 unless defined? PDTP::DEFAULT_PORT
29
+ def self.default_port() DEFAULT_PORT end
30
+ end
@@ -1,11 +1,23 @@
1
1
  #--
2
2
  # Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
3
- # All rights reserved. See COPYING for permissions.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
4
16
  #
5
17
  # This source file is distributed as part of the
6
18
  # DistribuStream file transfer system.
7
19
  #
8
- # See http://distribustream.rubyforge.org/
20
+ # See http://distribustream.org/
9
21
  #++
10
22
 
11
23
  module PDTP
@@ -0,0 +1,217 @@
1
+ # This module rewrites pieces of the very good Mongrel web server in
2
+ # order to change it from a threaded application to an event based
3
+ # application running inside an EventMachine event loop. It should
4
+ # be compatible with the existing Mongrel handlers for Rails,
5
+ # Camping, Nitro, etc....
6
+
7
+ require 'rubygems'
8
+ require 'eventmachine'
9
+ require 'mongrel'
10
+
11
+ module Mongrel
12
+ class Protocol < EventMachine::Connection
13
+ def post_init
14
+ @parser = HttpParser.new
15
+ @params = HttpParams.new
16
+ @nparsed = 0
17
+ @request = nil
18
+ @request_len = nil
19
+ @linebuffer = ''
20
+ end
21
+
22
+ def receive_data data
23
+ @linebuffer << data
24
+ @nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
25
+ if @parser.finished?
26
+ if @request_len.nil?
27
+ @request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
28
+ script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
29
+ if handlers
30
+ @params[::Mongrel::Const::PATH_INFO] = path_info
31
+ @params[::Mongrel::Const::SCRIPT_NAME] = script_name
32
+ @params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] || ::Socket.unpack_sockaddr_in(get_peername)[1]
33
+ @notifiers = handlers.select { |h| h.request_notify }
34
+ end
35
+ if @request_len > ::Mongrel::Const::MAX_BODY
36
+ new_buffer = Tempfile.new(::Mongrel::Const::MONGREL_TMP_BASE)
37
+ new_buffer.binmode
38
+ new_buffer << @linebuffer[@nparsed..-1]
39
+ @linebuffer = new_buffer
40
+ else
41
+ @linebuffer = StringIO.new(@linebuffer[@nparsed..-1])
42
+ @linebuffer.pos = @linebuffer.length
43
+ end
44
+ end
45
+ if @linebuffer.length >= @request_len
46
+ @linebuffer.rewind
47
+ ::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
48
+ @linebuffer.delete if Tempfile === @linebuffer
49
+ end
50
+ elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
51
+ close_connection
52
+ raise ::Mongrel::HttpParserError.new("HEADER is longer than allowed, aborting client early.")
53
+ end
54
+ rescue ::Mongrel::HttpParserError
55
+ if $mongrel_debug_client
56
+ STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
57
+ STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
58
+ end
59
+ close_connection
60
+ rescue Exception => e
61
+ close_connection
62
+ raise e
63
+ end
64
+
65
+ def write data
66
+ send_data data
67
+ end
68
+
69
+ def closed?
70
+ false
71
+ end
72
+
73
+ end
74
+
75
+ class HttpServer
76
+ def initialize(host, port, num_processors = 950, throttle = 0, timeout = 60)
77
+ @socket = nil
78
+ @classifier = Mongrel::URIClassifier.new
79
+ @host = host
80
+ @port = port
81
+ @workers = ThreadGroup.new
82
+ @timeout = timeout
83
+ @num_processors = num_processors
84
+ @death_time = 60
85
+ self.class.const_set(:Instance,self)
86
+ end
87
+
88
+ def run
89
+ trap('INT') { raise StopServer }
90
+ trap('TERM') { raise StopServer }
91
+ @acceptor = Thread.new do
92
+ EventMachine.run do
93
+ begin
94
+ run_evented
95
+ rescue StopServer
96
+ EventMachine.stop_event_loop
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def run_evented
103
+ EventMachine.start_server @host, @port, Mongrel::Protocol
104
+ end
105
+
106
+ def process_http_request(params,linebuffer,client)
107
+ if not params[Const::REQUEST_PATH]
108
+ uri = URI.parse(params[Const::REQUEST_URI])
109
+ params[Const::REQUEST_PATH] = uri.request_uri
110
+ end
111
+
112
+ raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
113
+
114
+ script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
115
+
116
+ if handlers
117
+ notifiers = handlers.select { |h| h.request_notify }
118
+ request = HttpRequest.new(params, linebuffer, notifiers)
119
+
120
+ # request is good so far, continue processing the response
121
+ response = HttpResponse.new(client)
122
+
123
+ # Process each handler in registered order until we run out or one finalizes the response.
124
+ dispatch_to_handlers(handlers,request,response)
125
+
126
+ # And finally, if nobody closed the response off, we finalize it.
127
+ unless response.done
128
+ response.finished unless response.pending
129
+ else
130
+ response.close_connection_after_writing
131
+ end
132
+ else
133
+ # Didn't find it, return a stock 404 response.
134
+ client.send_data(Const::ERROR_404_RESPONSE)
135
+ client.close_connection_after_writing
136
+ end
137
+ end
138
+
139
+ def dispatch_to_handlers(handlers,request,response)
140
+ handlers.each do |handler|
141
+ handler.process(request, response)
142
+ break if response.done
143
+ end
144
+ end
145
+ end
146
+
147
+ class HttpRequest
148
+ def initialize(params, linebuffer, dispatchers)
149
+ @params = params
150
+ @dispatchers = dispatchers
151
+ @body = linebuffer
152
+ end
153
+ end
154
+
155
+ class HttpResponse
156
+ # A flag to prevent response.finished from being called automatically
157
+ attr_accessor :pending
158
+
159
+ def send_file(path, small_file = false)
160
+ File.open(path, "rb") do |f|
161
+ while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
162
+ begin
163
+ write(chunk)
164
+ rescue Object => exc
165
+ break
166
+ end
167
+ end
168
+ end
169
+ @body_sent = true
170
+ end
171
+
172
+ def write(data)
173
+ @socket.send_data data
174
+ end
175
+
176
+ def close_connection_after_writing
177
+ @socket.close_connection_after_writing
178
+ end
179
+
180
+ def socket_error(details)
181
+ @socket.close_connection
182
+ done = true
183
+ raise details
184
+ end
185
+
186
+ def finished
187
+ send_status
188
+ send_header
189
+ send_body
190
+ @socket.close_connection_after_writing
191
+ end
192
+ end
193
+
194
+ class Configurator
195
+ # This version fixes a bug in the regular Mongrel version by adding
196
+ # initialization of groups.
197
+ def change_privilege(user, group)
198
+ if user and group
199
+ log "Initializing groups for {#user}:{#group}."
200
+ Process.initgroups(user,Etc.getgrnam(group).gid)
201
+ end
202
+
203
+ if group
204
+ log "Changing group to #{group}."
205
+ Process::GID.change_privilege(Etc.getgrnam(group).gid)
206
+ end
207
+
208
+ if user
209
+ log "Changing user to #{user}."
210
+ Process::UID.change_privilege(Etc.getpwnam(user).uid)
211
+ end
212
+ rescue Errno::EPERM
213
+ log "FAILED to change user:group #{user}:#{group}: #$!"
214
+ exit 1
215
+ end
216
+ end
217
+ end