1
'''All POSIX Type client support for Bcfg2'''
2
__revision__ = '$Revision: 2435 $'
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
8
import binascii, difflib, grp, os, pwd, xml.sax.saxutils
9
import Bcfg2.Client.Tools
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}]
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:
25
class POSIX(Bcfg2.Client.Tools.Tool):
26
'''POSIX File support code'''
28
__handles__ = [('ConfigFile', None), ('Directory', None), ('Permissions', None), \
30
__req__ = {'ConfigFile': ['name', 'owner', 'group', 'perms'],
31
'Directory': ['name', 'owner', 'group', 'perms'],
32
'Permissions': ['name', 'owner', 'group', 'perms'],
33
'SymLink': ['name', 'to']}
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'):
45
def VerifySymLink(self, entry, _):
46
'''Verify SymLink Entry'''
48
sloc = os.readlink(entry.get('name'))
49
if sloc == entry.get('to'):
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)
56
entry.set('current_exists', 'false')
59
def InstallSymLink(self, entry):
60
'''Install SymLink Entry'''
61
self.logger.info("Installing Symlink %s" % (entry.get('name')))
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" % \
67
os.unlink(entry.get('name'))
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')))
72
os.unlink(entry.get('name'))
74
self.logger.info("Symlink %s cleanup failed" % (entry.get('name')))
76
os.symlink(entry.get('to'), entry.get('name'))
81
def VerifyDirectory(self, entry, _):
82
'''Verify Directory Entry'''
83
while len(entry.get('perms', '')) < 4:
84
entry.set('perms', '0' + entry.get('perms', ''))
86
ondisk = os.stat(entry.get('name'))
88
entry.set('current_exists', 'false')
89
self.logger.debug("%s %s does not exist" %
90
(entry.tag, entry.get('name')))
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')
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'))):
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')))
117
def InstallDirectory(self, entry):
118
'''Install Directory Entry'''
119
self.logger.info("Installing Directory %s" % (entry.get('name')))
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')))
125
os.unlink(entry.get('name'))
127
self.logger.info("Failed to unlink %s" % (entry.get('name')))
130
self.logger.debug("Found a pre-existing directory at %s" % (entry.get('name')))
137
parent = "/".join(entry.get('name').split('/')[:-1])
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])
146
sloc = os.lstat(current)
148
if not S_ISDIR(sloc[ST_MODE]):
160
os.mkdir(entry.get('name'))
162
self.logger.error('Failed to create directory %s' % (entry.get('name')))
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')))
169
except (OSError, KeyError):
170
self.logger.error('Permission fixup failed for %s' % (entry.get('name')))
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':
182
if entry.text == None:
183
self.logger.error("Cannot verify incomplete ConfigFile %s" % (entry.get('name')))
185
tempdata = entry.text
188
content = open(entry.get('name')).read()
190
# file does not exist
192
contentStatus = content == tempdata
193
if not contentStatus:
194
diff = '\n'.join([x for x in difflib.ndiff(content.split('\n'), tempdata.split('\n'))])
196
entry.set("current_diff", xml.sax.saxutils.quoteattr(diff))
199
return contentStatus and permissionStatus
201
def InstallConfigFile(self, entry):
202
'''Install ConfigFile Entry'''
203
self.logger.info("Installing ConfigFile %s" % (entry.get('name')))
205
parent = "/".join(entry.get('name').split('/')[:-1])
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])
214
sloc = os.lstat(current)
216
if not S_ISDIR(sloc[ST_MODE]):
227
# If we get here, then the parent directory should exist
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':
235
filedata = entry.text
236
newfile.write(filedata)
239
os.chown(newfile.name, pwd.getpwnam(entry.get('owner'))[2],
240
grp.getgrnam(entry.get('group'))[2])
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'))
248
except (OSError, IOError), err:
250
self.logger.info("Failed to open %s for writing" % (entry.get('name')))