summaryrefslogtreecommitdiffstats
path: root/lib/puppet/suidmanager.rb
blob: b65b5eb60a688dd4aca3cfbd5c05bf585c511d5b (plain)
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
91
92
93
94
95
96
97
require 'facter'
require 'puppet/util/warnings'

module Puppet
    module SUIDManager
        include Puppet::Util::Warnings

        platform = Facter["kernel"].value
        [:uid=, :gid=, :uid, :gid].each do |method|
            define_method(method) do |*args|
                # NOTE: 'method' is closed here.
                newmethod = method

                if platform == "Darwin" and (method == :uid= or method == :gid=)
                    Puppet::Util::Warnings.warnonce "Cannot change real UID on Darwin"
                    newmethod = ("e" + method.to_s).intern
                end

                return Process.send(newmethod, *args)
            end
            module_function method
        end

        [:euid=, :euid, :egid=, :egid].each do |method|
            define_method(method) do |*args|
                Process.send(method, *args)
            end
            module_function method
        end

        def asuser(new_euid=nil, new_egid=nil)
            # Unless we're root, don't do a damn thing.
            unless Process.uid == 0
                return yield
            end
            old_egid = old_euid = nil
            if new_egid
                saved_state_egid = new_egid
                new_egid = Puppet::Util.gid(new_egid)
                if new_egid == nil
                  raise Puppet::Error, "Invalid group: %s" % saved_state_egid
                end
                old_egid = self.egid
                self.egid = new_egid
            end
            if new_euid
                saved_state_euid = new_euid
                new_euid = Puppet::Util.uid(new_euid)
                if new_euid == nil
                  raise Puppet::Error, "Invalid user: %s" % saved_state_euid
                end
                old_euid = self.euid
                self.euid = new_euid
            end

            return yield
        ensure
            self.euid = old_euid if old_euid
            self.egid = old_egid if old_egid
        end

        module_function :asuser

        def run_and_capture(command, new_uid=nil, new_gid=nil)
            output = nil

            asuser(new_uid, new_gid) do
                # capture both stdout and stderr unless we are on ruby < 1.8.4
                # NOTE: this would be much better facilitated with a specialized popen()
                #       (see the test suite for more details.)
                if new_uid and (Facter['rubyversion'].value <=> "1.8.4") < 0
                    Puppet::Util::Warnings.warnonce "Cannot capture STDERR when running as another user on Ruby < 1.8.4"
                    output = %x{#{command}}
                else
                    output = %x{#{command} 2>&1}
                end
            end

            [output, $?.dup]
        end

        module_function :run_and_capture

        def system(command, new_uid=nil, new_gid=nil)
            status = nil
            asuser(new_uid, new_gid) do
                Kernel.system(command)
                status = $?.dup
            end
            status
        end
        
        module_function :system
    end
end

# $Id$