boxgrinder-build 0.10.0 → 0.10.1

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 (63) hide show
  1. data/CHANGELOG +11 -0
  2. data/Manifest +6 -7
  3. data/Rakefile +11 -6
  4. data/bash_completion +37 -0
  5. data/bin/boxgrinder-build +20 -5
  6. data/boxgrinder-build.gemspec +4 -4
  7. data/lib/boxgrinder-build.rb +2 -1
  8. data/lib/boxgrinder-build/appliance.rb +26 -24
  9. data/lib/boxgrinder-build/helpers/augeas-helper.rb +1 -1
  10. data/lib/boxgrinder-build/helpers/ec2-helper.rb +2 -2
  11. data/lib/boxgrinder-build/helpers/guestfs-helper.rb +1 -1
  12. data/lib/boxgrinder-build/helpers/s3-helper.rb +2 -2
  13. data/lib/boxgrinder-build/managers/plugin-manager.rb +1 -1
  14. data/lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb +1 -1
  15. data/lib/boxgrinder-build/plugins/delivery/elastichosts/elastichosts-plugin.rb +2 -2
  16. data/lib/boxgrinder-build/plugins/delivery/openstack/openstack-plugin.rb +3 -3
  17. data/lib/boxgrinder-build/plugins/delivery/s3/s3-plugin.rb +16 -10
  18. data/lib/boxgrinder-build/plugins/os/centos/centos-plugin.rb +1 -1
  19. data/lib/boxgrinder-build/plugins/os/fedora/fedora-plugin.rb +1 -1
  20. data/lib/boxgrinder-build/plugins/os/rhel/rhel-plugin.rb +1 -1
  21. data/lib/boxgrinder-build/plugins/os/rpm-based/kickstart.rb +5 -1
  22. data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin.rb +8 -3
  23. data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-dependency-validator.rb +1 -1
  24. data/lib/boxgrinder-build/plugins/os/sl/sl-plugin.rb +1 -1
  25. data/lib/boxgrinder-build/plugins/platform/ec2/ec2-plugin.rb +1 -1
  26. data/lib/boxgrinder-build/util/concurrent/get_set.rb +46 -0
  27. data/lib/boxgrinder-build/util/permissions/fs-monitor.rb +182 -0
  28. data/lib/boxgrinder-build/util/permissions/fs-observer.rb +82 -0
  29. data/lib/boxgrinder-build/util/permissions/user-switcher.rb +42 -0
  30. data/rubygem-boxgrinder-build.spec +25 -3
  31. data/spec/appliance-spec.rb +69 -82
  32. data/spec/helpers/augeas-helper-spec.rb +0 -2
  33. data/spec/helpers/guestfs-helper-spec.rb +1 -3
  34. data/spec/helpers/image-helper-spec.rb +0 -2
  35. data/spec/helpers/linux-helper-spec.rb +0 -2
  36. data/spec/helpers/package-helper-spec.rb +0 -2
  37. data/spec/helpers/plugin-helper-spec.rb +0 -2
  38. data/spec/helpers/s3-helper-spec.rb +0 -2
  39. data/spec/managers/plugin-manager-spec.rb +0 -2
  40. data/spec/plugins/base-plugin-spec.rb +0 -2
  41. data/spec/plugins/delivery/ebs/ebs-plugin-spec.rb +0 -2
  42. data/spec/plugins/delivery/elastichosts/elastichosts-plugin-spec.rb +3 -5
  43. data/spec/plugins/delivery/libvirt/libvirt-plugin-spec.rb +19 -17
  44. data/spec/plugins/delivery/local/local-plugin-spec.rb +0 -2
  45. data/spec/plugins/delivery/s3/s3-plugin-spec.rb +6 -8
  46. data/spec/plugins/delivery/sftp/sftp-plugin-spec.rb +0 -2
  47. data/spec/plugins/os/centos/centos-plugin-spec.rb +0 -2
  48. data/spec/plugins/os/fedora/fedora-plugin-spec.rb +0 -2
  49. data/spec/plugins/os/rhel/rhel-plugin-spec.rb +0 -2
  50. data/spec/plugins/os/rpm-based/kickstart-spec.rb +0 -2
  51. data/spec/plugins/os/rpm-based/rpm-based-os-plugin-spec.rb +16 -4
  52. data/spec/plugins/os/rpm-based/rpm-dependency-validator-spec.rb +0 -2
  53. data/spec/plugins/platform/ec2/ec2-plugin-spec.rb +0 -2
  54. data/spec/plugins/platform/virtualbox/virtualbox-plugin-spec.rb +1 -3
  55. data/spec/plugins/platform/virtualpc/virtualpc-plugin-spec.rb +0 -1
  56. data/spec/plugins/platform/vmware/vmware-plugin-spec.rb +25 -24
  57. data/spec/rcov_helper.rb +2 -0
  58. data/spec/spec_helper.rb +9 -0
  59. data/spec/util/concurrent/get-set-spec.rb +43 -0
  60. data/spec/util/permissions/fs-monitor-spec.rb +233 -0
  61. data/spec/util/permissions/fs-observer-spec.rb +141 -0
  62. data/spec/util/permissions/user-switcher-spec.rb +69 -0
  63. metadata +20 -5
@@ -20,7 +20,7 @@ require 'boxgrinder-build/plugins/os/rhel/rhel-plugin'
20
20
 
21
21
  module BoxGrinder
22
22
  class CentOSPlugin < RHELPlugin
23
- plugin :type => :os, :name => :centos, :full_name => "CentOS", :versions => ["5", "6"]
23
+ plugin :type => :os, :name => :centos, :full_name => "CentOS", :versions => ["5", "6"], :require_root => true
24
24
 
25
25
  def after_init
26
26
  super
@@ -20,7 +20,7 @@ require 'boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin'
20
20
 
21
21
  module BoxGrinder
22
22
  class FedoraPlugin < RPMBasedOSPlugin
23
- plugin :type => :os, :name => :fedora, :full_name => "Fedora", :versions => ["13", "14", "15", "16", "rawhide"]
23
+ plugin :type => :os, :name => :fedora, :full_name => "Fedora", :versions => ["13", "14", "15", "16", "rawhide"], :require_root => true
24
24
 
25
25
  def after_init
26
26
  super
@@ -20,7 +20,7 @@ require 'boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin'
20
20
 
21
21
  module BoxGrinder
22
22
  class RHELPlugin < RPMBasedOSPlugin
23
- plugin :type => :os, :name => :rhel, :full_name => "Red Hat Enterprise Linux", :versions => ['5', '6']
23
+ plugin :type => :os, :name => :rhel, :full_name => "Red Hat Enterprise Linux", :versions => ['5', '6'], :require_root => true
24
24
 
25
25
  def after_init
26
26
  super
@@ -37,7 +37,7 @@ module BoxGrinder
37
37
 
38
38
  def create
39
39
  template = "#{File.dirname(__FILE__)}/src/appliance.ks.erb"
40
- kickstart = ERB.new(File.read(template)).result(build_definition.send(:binding))
40
+ kickstart = ERB.new(File.read(template)).result(build_definition.get_binding)
41
41
  File.open(@kickstart_file, 'w') { |f| f.write(kickstart) }
42
42
 
43
43
  @kickstart_file
@@ -54,6 +54,10 @@ module BoxGrinder
54
54
  self[sym.to_s]
55
55
  end
56
56
 
57
+ def definition.get_binding
58
+ binding
59
+ end
60
+
57
61
  cost = 40
58
62
 
59
63
  definition['mount_points'] = @linux_helper.partition_mount_points(@appliance_config.hardware.partitions)
@@ -147,7 +147,7 @@ module BoxGrinder
147
147
  @log.debug "Cleaning appliance-creator mount points..."
148
148
 
149
149
  Dir["#{@dir.tmp}/imgcreate-*"].each do |dir|
150
- dev_mapper = @exec_helper.execute "mount | grep #{dir} | awk '{print $1}'"
150
+ dev_mapper = @exec_helper.execute("mount | grep #{dir} | awk '{print $1}'").split("\n")
151
151
 
152
152
  mappings = {}
153
153
 
@@ -158,7 +158,7 @@ module BoxGrinder
158
158
  end
159
159
  end
160
160
 
161
- (['/var/cache/yum', '/dev/shm', '/dev/pts', '/proc', '/sys'] + @appliance_config.hardware.partitions.keys.reverse).each do |mount_point|
161
+ (['/var/cache/yum', '/dev/shm', '/dev/pts', '/proc', '/sys'] + @appliance_config.hardware.partitions.keys.sort.reverse).each do |mount_point|
162
162
  @log.trace "Unmounting '#{mount_point}'..."
163
163
  @exec_helper.execute "umount -d #{dir}/install_root#{mount_point}"
164
164
  end
@@ -251,6 +251,11 @@ module BoxGrinder
251
251
 
252
252
  def install_repos(guestfs)
253
253
  @log.debug "Installing repositories from appliance definition file..."
254
+
255
+ # It seems that the directory is not always created by default if default repos are inhibited (e.g. SL6)
256
+ yum_d = '/etc/yum.repos.d/'
257
+ guestfs.mkdir_p(yum_d) if guestfs.exists(yum_d) == 0
258
+
254
259
  @appliance_config.repos.each do |repo|
255
260
  if repo['ephemeral']
256
261
  @log.debug "Repository '#{repo['name']}' is an ephemeral repo. It'll not be installed in the appliance."
@@ -264,7 +269,7 @@ module BoxGrinder
264
269
  repo_file << ("#{type}=#{repo[type]}\n") unless repo[type].nil?
265
270
  end
266
271
 
267
- guestfs.write_file("/etc/yum.repos.d/#{repo['name']}.repo", repo_file, 0)
272
+ guestfs.write_file("#{yum_d}#{repo['name']}.repo", repo_file, 0)
268
273
  end
269
274
  @log.debug "Repositories installed."
270
275
  end
@@ -80,7 +80,7 @@ module BoxGrinder
80
80
  for name in package_list
81
81
  found = false
82
82
 
83
- repoquery_output.each do |line|
83
+ repoquery_output.each_line do |line|
84
84
  line = line.strip
85
85
 
86
86
  package = line.match( /^([\S]+)-\d+:/ )
@@ -20,7 +20,7 @@ require 'boxgrinder-build/plugins/os/rhel/rhel-plugin'
20
20
 
21
21
  module BoxGrinder
22
22
  class ScientificLinuxPlugin < RHELPlugin
23
- plugin :type => :os, :name => :sl, :full_name => "Scientific Linux", :versions => ["5", "6"]
23
+ plugin :type => :os, :name => :sl, :full_name => "Scientific Linux", :versions => ["5", "6"], :require_root => true
24
24
 
25
25
  SL_REPOS = {
26
26
  "5" => {
@@ -22,7 +22,7 @@ require 'tempfile'
22
22
 
23
23
  module BoxGrinder
24
24
  class EC2Plugin < BasePlugin
25
- plugin :type => :platform, :name => :ec2, :full_name => "Amazon Elastic Compute Cloud (Amazon EC2)"
25
+ plugin :type => :platform, :name => :ec2, :full_name => "Amazon Elastic Compute Cloud (Amazon EC2)", :require_root => true
26
26
 
27
27
  def after_init
28
28
  register_deliverable(:disk => "#{@appliance_config.name}.ec2")
@@ -0,0 +1,46 @@
1
+ #
2
+ # Copyright 2012 Red Hat, Inc.
3
+ #
4
+ # This is free software; you can redistribute it and/or modify it
5
+ # under the terms of the GNU Lesser General Public License as
6
+ # published by the Free Software Foundation; either version 3 of
7
+ # the License, or (at your option) any later version.
8
+ #
9
+ # This software 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 GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this software; if not, write to the Free
16
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
+
19
+ require 'thread'
20
+
21
+ class GetSet
22
+ def initialize(initial_state=false)
23
+ @val = initial_state
24
+ @mutex = Mutex.new
25
+ end
26
+
27
+ # Atomic get-and-set.
28
+ #
29
+ # When used with a block, the existing value is provided as
30
+ # an argument to the block. The block's return value sets the
31
+ # object's value state.
32
+ #
33
+ # When used without a block; if a nil +set_val+ parameter is
34
+ # provided the existing state is returned. Else the object
35
+ # value state is set to +set_val+
36
+ def get_set(set_val=nil, &blk)
37
+ @mutex.synchronize do
38
+ if block_given?
39
+ @val = blk.call(@val)
40
+ else
41
+ @val = set_val unless set_val.nil?
42
+ end
43
+ @val
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,182 @@
1
+ #
2
+ # Copyright 2012 Red Hat, Inc.
3
+ #
4
+ # This is free software; you can redistribute it and/or modify it
5
+ # under the terms of the GNU Lesser General Public License as
6
+ # published by the Free Software Foundation; either version 3 of
7
+ # the License, or (at your option) any later version.
8
+ #
9
+ # This software 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 GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this software; if not, write to the Free
16
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
+
19
+ require 'thread'
20
+ require 'observer'
21
+ require 'pathname'
22
+ require 'singleton'
23
+
24
+ require 'boxgrinder-build/util/concurrent/get_set'
25
+
26
+ module BoxGrinder
27
+ class FSMonitor
28
+ include Singleton
29
+ include Observable
30
+
31
+ def initialize
32
+ @flag = GetSet.new
33
+ @lock_a = Mutex.new
34
+ @lock_b = Mutex.new
35
+ set_hooks
36
+ end
37
+
38
+ # Start capturing paths. Providing a block automatically stops the
39
+ # capture process upon termination of the scope.
40
+ #
41
+ # @param Array<#update> Observers to be notified of capture
42
+ # events. Each observer should expect a hash{} containing a
43
+ # +:command+, and potentially +:data+.
44
+ #
45
+ # @yield Block that automatically calls #stop at the end of scope
46
+ # to cease capture.
47
+ def capture(*observers, &block)
48
+ @lock_a.synchronize do
49
+ add_observers(observers)
50
+ _capture(&block)
51
+
52
+ if block_given?
53
+ yield
54
+ _stop
55
+ end
56
+ end
57
+ end
58
+
59
+ # Explicitly stop capturing paths. This should be utilised if
60
+ # capture was not used with a block. Fires the +:stop_capture+
61
+ # command to indicate that capturing has ceased.
62
+ def stop
63
+ @lock_a.synchronize { _stop }
64
+ end
65
+
66
+ # Stop any capturing and delete all observers. Useful for testing.
67
+ # @see #stop
68
+ def reset
69
+ @lock_a.synchronize do
70
+ _stop
71
+ delete_observers
72
+ end
73
+ end
74
+
75
+ # Add a path string. Called by the hooked methods when an
76
+ # applicable action is triggered and capturing is enabled. Fires
77
+ # the +:add_path+ command, and includes the full path as +:data+.
78
+ #
79
+ # If no observers have been assigned before a path is added, they
80
+ # will be silently lost.
81
+ #
82
+ # @param [String] path Filesystem path.
83
+ # @return [Boolean] False if no observers were present.
84
+ def add_path(path)
85
+ @lock_b.synchronize do
86
+ changed(true)
87
+ notify_observers(:command => :add_path, :data => realpath(path))
88
+ end
89
+ end
90
+
91
+ # Trigger ownership change immediately, but without ceasing. Fires
92
+ # the +:chown+ command on all observers.
93
+ #
94
+ # @return [boolean] False if no observers were present.
95
+ def trigger
96
+ changed(true)
97
+ notify_observers(:command => :chown)
98
+ end
99
+
100
+ private # Not threadsafe
101
+
102
+ # The hooks will all use the same get-and-set to determine when to
103
+ # begin/cease capturing paths.
104
+ def _capture
105
+ @flag.get_set(true)
106
+ end
107
+
108
+ def _stop
109
+ @flag.get_set(false)
110
+ changed(true)
111
+ notify_observers(:command => :stop_capture)
112
+ end
113
+
114
+ # Hooks to capture any standard file, link or directory creation. Other
115
+ # methods (e.g. FileUtils#mkdir_p, FileUtils#move), ultimately bottom out
116
+ # into these primitive functions.
117
+ def set_hooks
118
+ # Final splat var captures any other variables we are not interested in,
119
+ # and avoids them being squashed into the final var.
120
+ eigen_capture(File, [:open, :new], @flag) do |klazz, path, mode, *other|
121
+ add_path(path) if klazz == File && mode =~ /^(t|b)?((w|a)[+]?)(t|b)?$/
122
+ end
123
+
124
+ eigen_capture(File, [:rename, :symlink, :link], @flag) do |klazz, old, new, *other|
125
+ add_path(new)
126
+ end
127
+
128
+ eigen_capture(Dir, :mkdir, @flag) do |klazz, path, *other|
129
+ add_path(root_dir(path))
130
+ end
131
+ end
132
+
133
+ # Hooks into class methods by accessing the eigenclass (virtual class).
134
+ def eigen_capture(klazz, m_sym, flag, &blk)
135
+ v_klazz = (class << klazz; self; end)
136
+ instance_capture(v_klazz, m_sym, flag, &blk)
137
+ end
138
+
139
+ def instance_capture(klazz, m_sym, flag, &blk)
140
+ Array(m_sym).each{ |sym| alias_and_capture(klazz, sym, flag, &blk) }
141
+ end
142
+
143
+ # Cracks open the target class, and injects a wrapper to enable
144
+ # monitoring. By aliasing the original method the wrapper intercepts the
145
+ # call, which it forwards onto the 'real' method before executing the hook.
146
+ #
147
+ # The hook's functionality is provided via a &block, which is passed the
148
+ # caller's +self+ in addition to the wrapped method's parameters.
149
+ #
150
+ # Locking the +flag+ signals the hook to begin capturing.
151
+ def alias_and_capture(klazz, m_sym, flag, &blk)
152
+ alias_m_sym = "__alias_#{m_sym}"
153
+
154
+ klazz.class_eval do
155
+ alias_method alias_m_sym, m_sym
156
+
157
+ define_method(m_sym) do |*args, &blx|
158
+ response = send(alias_m_sym, *args, &blx)
159
+ blk.call(self, *args) if flag.get_set
160
+ response
161
+ end
162
+ end
163
+ end
164
+
165
+ def add_observers(observers)
166
+ observers.each{ |o| add_observer(o) unless o.nil? }
167
+ end
168
+
169
+ # Transform relative to absolute path
170
+ def realpath(path)
171
+ Pathname.new(path).realpath.to_s
172
+ end
173
+
174
+ # For a path relative such as 'x/y/z' returns 'x'. Useful for #mkdir_p
175
+ # where the entire new path is returned at once.
176
+ def root_dir(relpath)
177
+ r = relpath.match(%r(^[/]?.+?[/$]))
178
+ return relpath if r.nil?
179
+ r[0] || relpath
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,82 @@
1
+ #
2
+ # Copyright 2012 Red Hat, Inc.
3
+ #
4
+ # This is free software; you can redistribute it and/or modify it
5
+ # under the terms of the GNU Lesser General Public License as
6
+ # published by the Free Software Foundation; either version 3 of
7
+ # the License, or (at your option) any later version.
8
+ #
9
+ # This software 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 GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this software; if not, write to the Free
16
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
+
19
+ require 'fileutils'
20
+ require 'set'
21
+
22
+ module BoxGrinder
23
+ class FSObserver
24
+ attr_accessor :path_set
25
+ attr_accessor :filter_set
26
+
27
+ # @param [Integer] user The uid to switch from root to
28
+ # @param [Integer] group The gid to switch from root to
29
+ # @param [Hash] opts The options to create a observer with
30
+ # @option opts [Array<String>] :paths Additional paths to change
31
+ # ownership of.
32
+ # @option opts [String] :paths Additional path to to change
33
+ # ownership of
34
+ def initialize(user, group, opts={})
35
+ @path_set = Set.new(Array(opts[:paths]))
36
+ # Filter some default directories, plus any subdirectories of
37
+ # paths we discover at runtime
38
+ @filter_set = Set.new([%r(^/(etc|dev|sys|bin|sbin|etc|lib|lib64|boot|run|proc|selinux|tmp)(/|$))])
39
+ @user = user
40
+ @group = group
41
+ end
42
+
43
+ # Receives updates from FSMonitor#add_path
44
+ #
45
+ # @param [Hash] opts The options to update the observer
46
+ # @option opts [:symbol] :command The command to instruct the
47
+ # observer to execute.
48
+ # * +:add_path+ Indicates the +:data+ field contains a path.
49
+ # * +:stop_capture+ indicates that capturing has ceased. The
50
+ # observer will change ownership of the files, and switch
51
+ # to the user specified at #initialize.
52
+ # @option opts [String] :data Contains a resource path when the
53
+ # * +:add_path+ Command is called, otherwise ignored.
54
+ def update(update={})
55
+ case update[:command]
56
+ when :add_path
57
+ unless match_filter?(update[:data])
58
+ @path_set.add(update[:data])
59
+ @filter_set.merge(subdirectory_regex(update[:data]))
60
+ end
61
+ when :stop_capture, :chown
62
+ do_chown
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def do_chown
69
+ @path_set.each do |p|
70
+ FileUtils.chown_R(@user, @group, p, :force => true) if File.exist?(p)
71
+ end
72
+ end
73
+
74
+ def match_filter?(path)
75
+ @filter_set.inject(false){ |accum, filter| accum || !!(path =~ filter) }
76
+ end
77
+
78
+ def subdirectory_regex(paths)
79
+ Array(paths).collect{ |p| Regexp.new("^#{p}/") }
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ #
2
+ # Copyright 2012 Red Hat, Inc.
3
+ #
4
+ # This is free software; you can redistribute it and/or modify it
5
+ # under the terms of the GNU Lesser General Public License as
6
+ # published by the Free Software Foundation; either version 3 of
7
+ # the License, or (at your option) any later version.
8
+ #
9
+ # This software 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 GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this software; if not, write to the Free
16
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18
+
19
+ require 'tmpdir'
20
+
21
+ module BoxGrinder
22
+ class UserSwitcher
23
+
24
+ def UserSwitcher.change_user(u, g, &blk)
25
+ prev_u, prev_g = Process.uid, Process.gid
26
+ set_user(u, g)
27
+ blk.call
28
+ set_user(prev_u, prev_g)
29
+ end
30
+
31
+ private
32
+
33
+ # Working around bugs.... we can rely on the saved id to be able
34
+ # to sneak back to the previous user later.
35
+ def UserSwitcher.set_user(u, g)
36
+ return if Process.uid == u && Process.gid == g
37
+ # If already set to the given value
38
+ Process.egid, Process.gid = g, g
39
+ Process.euid, Process.uid = u, u
40
+ end
41
+ end
42
+ end