~ubuntu-branches/ubuntu/hardy/bcfg2/hardy-updates

« back to all changes in this revision

Viewing changes to src/lib/Client/Tools/POSIX.py

  • Committer: Bazaar Package Importer
  • Author(s): Sami Haahtinen
  • Date: 2006-11-16 22:39:16 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061116223916-8dtn3t86cz58vg2x
Tags: 0.8.6.1-1
* New Upstream Release
* Replaced faulty if clause in bcfg2.postrm (Closes: #398772)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
'''All POSIX Type client support for Bcfg2'''
 
2
__revision__ = '$Revision: 2435 $'
 
3
 
 
4
from stat import S_ISVTX, S_ISGID, S_ISUID, S_IXUSR, S_IWUSR, S_IRUSR, S_IXGRP
 
5
from stat import S_IWGRP, S_IRGRP, S_IXOTH, S_IWOTH, S_IROTH, ST_MODE, S_ISDIR
 
6
from stat import S_IFREG, ST_UID, ST_GID, S_ISREG, S_IFDIR, S_ISLNK
 
7
 
 
8
import binascii, difflib, grp, os, pwd, xml.sax.saxutils
 
9
import Bcfg2.Client.Tools
 
10
 
 
11
def calcPerms(initial, perms):
 
12
    '''This compares ondisk permissions with specified ones'''
 
13
    pdisp = [{1:S_ISVTX, 2:S_ISGID, 4:S_ISUID}, {1:S_IXUSR, 2:S_IWUSR, 4:S_IRUSR},
 
14
             {1:S_IXGRP, 2:S_IWGRP, 4:S_IRGRP}, {1:S_IXOTH, 2:S_IWOTH, 4:S_IROTH}]
 
15
    tempperms = initial
 
16
    if len(perms) == 3:
 
17
        perms = '0%s' % (perms)
 
18
    pdigits = [int(perms[digit]) for digit in range(4)]
 
19
    for index in range(4):
 
20
        for (num, perm) in pdisp[index].iteritems():
 
21
            if pdigits[index] & num:
 
22
                tempperms |= perm
 
23
    return tempperms
 
24
 
 
25
class POSIX(Bcfg2.Client.Tools.Tool):
 
26
    '''POSIX File support code'''
 
27
    __name__ = 'POSIX'
 
28
    __handles__ = [('ConfigFile', None), ('Directory', None), ('Permissions', None), \
 
29
                   ('SymLink', None)]
 
30
    __req__ = {'ConfigFile': ['name', 'owner', 'group', 'perms'],
 
31
               'Directory': ['name', 'owner', 'group', 'perms'],
 
32
               'Permissions': ['name', 'owner', 'group', 'perms'],
 
33
               'SymLink': ['name', 'to']}
 
34
 
 
35
    def canInstall(self, entry):
 
36
        '''Check if entry is complete for installation'''
 
37
        if Bcfg2.Client.Tools.Tool.canInstall(self, entry):
 
38
            if (entry.tag, entry.text, entry.get('empty', 'false')) == \
 
39
               ('ConfigFile', None, 'false'):
 
40
                return False
 
41
            return True
 
42
        else:
 
43
            return False
 
44
 
 
45
    def VerifySymLink(self, entry, _):
 
46
        '''Verify SymLink Entry'''
 
47
        try:
 
48
            sloc = os.readlink(entry.get('name'))
 
49
            if sloc == entry.get('to'):
 
50
                return True
 
51
            self.logger.debug("Symlink %s points to %s, should be %s" % \
 
52
                              (entry.get('name'), sloc, entry.get('to')))
 
53
            entry.set('current_to', sloc)
 
54
            return False
 
55
        except OSError:
 
56
            entry.set('current_exists', 'false')
 
57
            return False
 
58
 
 
59
    def InstallSymLink(self, entry):
 
60
        '''Install SymLink Entry'''
 
61
        self.logger.info("Installing Symlink %s" % (entry.get('name')))
 
62
        try:
 
63
            fmode = os.lstat(entry.get('name'))[ST_MODE]
 
64
            if S_ISREG(fmode) or S_ISLNK(fmode):
 
65
                self.logger.debug("Non-directory entry already exists at %s" % \
 
66
                                  (entry.get('name')))
 
67
                os.unlink(entry.get('name'))
 
68
            elif S_ISDIR(fmode):
 
69
                self.logger.debug("Directory entry already exists at %s" % (entry.get('name')))
 
70
                self.cmd.run("mv %s/ %s.bak" % (entry.get('name'), entry.get('name')))
 
71
            else:
 
72
                os.unlink(entry.get('name'))
 
73
        except OSError:
 
74
            self.logger.info("Symlink %s cleanup failed" % (entry.get('name')))
 
75
        try:
 
76
            os.symlink(entry.get('to'), entry.get('name'))
 
77
            return True
 
78
        except OSError:
 
79
            return False
 
80
 
 
81
    def VerifyDirectory(self, entry, _):
 
82
        '''Verify Directory Entry'''
 
83
        while len(entry.get('perms', '')) < 4:
 
84
            entry.set('perms', '0' + entry.get('perms', ''))
 
85
        try:
 
86
            ondisk = os.stat(entry.get('name'))
 
87
        except OSError:
 
88
            entry.set('current_exists', 'false')
 
89
            self.logger.debug("%s %s does not exist" %
 
90
                              (entry.tag, entry.get('name')))
 
91
            return False
 
92
        try:
 
93
            owner = pwd.getpwuid(ondisk[ST_UID])[0]
 
94
            group = grp.getgrgid(ondisk[ST_GID])[0]
 
95
        except (OSError, KeyError):
 
96
            self.logger.error('User resolution failing')
 
97
            owner = 'root'
 
98
            group = 'root'
 
99
        perms = oct(os.stat(entry.get('name'))[ST_MODE])[-4:]
 
100
        if ((owner == entry.get('owner')) and
 
101
            (group == entry.get('group')) and
 
102
            (perms == entry.get('perms'))):
 
103
            return True
 
104
        else:
 
105
            if owner != entry.get('owner'):
 
106
                entry.set('current_owner', owner)
 
107
                self.logger.debug("%s %s ownership wrong" % (entry.tag, entry.get('name')))
 
108
            if group != entry.get('group'):
 
109
                entry.set('current_group', group)
 
110
                self.logger.debug("%s %s group wrong" % (entry.tag, entry.get('name')))
 
111
            if perms != entry.get('perms'):
 
112
                entry.set('current_perms', perms)
 
113
                self.logger.debug("%s %s permissions wrong: are %s should be %s" %
 
114
                               (entry.tag, entry.get('name'), perms, entry.get('perms')))
 
115
            return False
 
116
 
 
117
    def InstallDirectory(self, entry):
 
118
        '''Install Directory Entry'''
 
119
        self.logger.info("Installing Directory %s" % (entry.get('name')))
 
120
        try:
 
121
            fmode = os.lstat(entry.get('name'))
 
122
            if not S_ISDIR(fmode[ST_MODE]):
 
123
                self.logger.debug("Found a non-directory entry at %s" % (entry.get('name')))
 
124
                try:
 
125
                    os.unlink(entry.get('name'))
 
126
                except OSError:
 
127
                    self.logger.info("Failed to unlink %s" % (entry.get('name')))
 
128
                    return False
 
129
            else:
 
130
                self.logger.debug("Found a pre-existing directory at %s" % (entry.get('name')))
 
131
                exists = True
 
132
        except OSError:
 
133
            # stat failed
 
134
            exists = False
 
135
 
 
136
        if not exists:
 
137
            parent = "/".join(entry.get('name').split('/')[:-1])
 
138
            if parent:
 
139
                try:
 
140
                    os.lstat(parent)
 
141
                except:
 
142
                    self.logger.debug('Creating parent path for directory %s' % (entry.get('name')))
 
143
                    for idx in xrange(len(parent.split('/')[:-1])):
 
144
                        current = '/'+'/'.join(parent.split('/')[1:2+idx])
 
145
                        try:
 
146
                            sloc = os.lstat(current)
 
147
                            try:
 
148
                                if not S_ISDIR(sloc[ST_MODE]):
 
149
                                    os.unlink(current)
 
150
                                    os.mkdir(current)
 
151
                            except OSError:
 
152
                                return False
 
153
                        except OSError:
 
154
                            try:
 
155
                                os.mkdir(current)
 
156
                            except OSError:
 
157
                                return False
 
158
 
 
159
            try:
 
160
                os.mkdir(entry.get('name'))
 
161
            except OSError:
 
162
                self.logger.error('Failed to create directory %s' % (entry.get('name')))
 
163
                return False
 
164
        try:
 
165
            os.chown(entry.get('name'),
 
166
                  pwd.getpwnam(entry.get('owner'))[2], grp.getgrnam(entry.get('group'))[2])
 
167
            os.chmod(entry.get('name'), calcPerms(S_IFDIR, entry.get('perms')))
 
168
            return True
 
169
        except (OSError, KeyError):
 
170
            self.logger.error('Permission fixup failed for %s' % (entry.get('name')))
 
171
            return False
 
172
 
 
173
    def VerifyConfigFile(self, entry, _):
 
174
        '''Install ConfigFile Entry'''
 
175
        # configfile verify is permissions check + content check
 
176
        permissionStatus = self.VerifyDirectory(entry, _)
 
177
        if entry.get('encoding', 'ascii') == 'base64':
 
178
            tempdata = binascii.a2b_base64(entry.text)
 
179
        elif entry.get('empty', 'false') == 'true':
 
180
            tempdata = ''
 
181
        else:
 
182
            if entry.text == None:
 
183
                self.logger.error("Cannot verify incomplete ConfigFile %s" % (entry.get('name')))
 
184
                return False
 
185
            tempdata = entry.text
 
186
 
 
187
        try:
 
188
            content = open(entry.get('name')).read()
 
189
        except IOError:
 
190
            # file does not exist
 
191
            return False
 
192
        contentStatus = content == tempdata
 
193
        if not contentStatus:
 
194
            diff = '\n'.join([x for x in difflib.ndiff(content.split('\n'), tempdata.split('\n'))])
 
195
            try:
 
196
                entry.set("current_diff", xml.sax.saxutils.quoteattr(diff))
 
197
            except:
 
198
                pass
 
199
        return contentStatus and permissionStatus
 
200
 
 
201
    def InstallConfigFile(self, entry):
 
202
        '''Install ConfigFile Entry'''
 
203
        self.logger.info("Installing ConfigFile %s" % (entry.get('name')))
 
204
 
 
205
        parent = "/".join(entry.get('name').split('/')[:-1])
 
206
        if parent:
 
207
            try:
 
208
                os.lstat(parent)
 
209
            except:
 
210
                self.logger.debug('Creating parent path for config file %s' % (entry.get('name')))
 
211
                for idx in xrange(len(parent.split('/')[:-1])):
 
212
                    current = '/'+'/'.join(parent.split('/')[1:2+idx])
 
213
                    try:
 
214
                        sloc = os.lstat(current)
 
215
                        try:
 
216
                            if not S_ISDIR(sloc[ST_MODE]):
 
217
                                os.unlink(current)
 
218
                                os.mkdir(current)
 
219
                        except OSError:
 
220
                            return False
 
221
                    except OSError:
 
222
                        try:
 
223
                            os.mkdir(current)
 
224
                        except OSError:
 
225
                            return False
 
226
 
 
227
        # If we get here, then the parent directory should exist
 
228
        try:
 
229
            newfile = open("%s.new"%(entry.get('name')), 'w')
 
230
            if entry.get('encoding', 'ascii') == 'base64':
 
231
                filedata = binascii.a2b_base64(entry.text)
 
232
            elif entry.get('empty', 'false') == 'true':
 
233
                filedata = ''
 
234
            else:
 
235
                filedata = entry.text
 
236
            newfile.write(filedata)
 
237
            newfile.close()
 
238
            try:
 
239
                os.chown(newfile.name, pwd.getpwnam(entry.get('owner'))[2],
 
240
                         grp.getgrnam(entry.get('group'))[2])
 
241
            except KeyError:
 
242
                os.chown(newfile.name, 0, 0)
 
243
            os.chmod(newfile.name, calcPerms(S_IFREG, entry.get('perms')))
 
244
            if entry.get("paranoid", False) and self.setup.get("paranoid", False):
 
245
                self.cmd.run("cp %s /var/cache/bcfg2/%s" % (entry.get('name')))
 
246
            os.rename(newfile.name, entry.get('name'))
 
247
            return True
 
248
        except (OSError, IOError), err:
 
249
            if err.errno == 13:
 
250
                self.logger.info("Failed to open %s for writing" % (entry.get('name')))
 
251
            else:
 
252
                print err
 
253
            return False