From 3cd34d135d6f3705862f0d1625c06a1669ec0023 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Thu, 1 Oct 2009 16:47:07 +0000 Subject: POSIX: Add block/character/fifo devices (ticket #477) This commit adds support for posix device types via Path entries in Bundler. The 'type' attribute for Path entries is now all lowercase. Signed-off-by: Sol Jerome git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@5470 ce84e21b-d406-0410-9b95-82705330c041 --- schemas/pathentry.xsd | 3 - schemas/rules.xsd | 13 +++++ schemas/types.xsd | 2 +- src/lib/Client/Tools/POSIX.py | 101 ++++++++++++++++++++++++++++++---- src/lib/Server/Plugins/POSIXCompat.py | 16 +++--- 5 files changed, 112 insertions(+), 23 deletions(-) diff --git a/schemas/pathentry.xsd b/schemas/pathentry.xsd index 5e129fa75..7be0e37e2 100644 --- a/schemas/pathentry.xsd +++ b/schemas/pathentry.xsd @@ -8,10 +8,7 @@ - - - diff --git a/schemas/rules.xsd b/schemas/rules.xsd index 0baa1e25f..d9b99d63d 100644 --- a/schemas/rules.xsd +++ b/schemas/rules.xsd @@ -33,6 +33,18 @@ + + + + + + + + + + + + @@ -63,6 +75,7 @@ + diff --git a/schemas/types.xsd b/schemas/types.xsd index db85bbaab..f04c3140f 100644 --- a/schemas/types.xsd +++ b/schemas/types.xsd @@ -24,7 +24,7 @@ - + diff --git a/src/lib/Client/Tools/POSIX.py b/src/lib/Client/Tools/POSIX.py index d956abf3a..917dc6620 100644 --- a/src/lib/Client/Tools/POSIX.py +++ b/src/lib/Client/Tools/POSIX.py @@ -12,11 +12,20 @@ import logging import os import pwd import shutil +import stat import string import time import Bcfg2.Client.Tools import Bcfg2.Options +log = logging.getLogger('posix') + +# map between dev_type attribute and stat constants +device_map = {'block': stat.S_IFBLK, + 'char': stat.S_IFCHR, + 'fifo': stat.S_IFIFO} + + def calcPerms(initial, perms): '''This compares ondisk permissions with specified ones''' pdisp = [{1:S_ISVTX, 2:S_ISGID, 4:S_ISUID}, @@ -33,8 +42,6 @@ def calcPerms(initial, perms): tempperms |= perm return tempperms -log = logging.getLogger('posix') - def normUid(entry): ''' This takes a user name or uid and @@ -82,7 +89,7 @@ class POSIX(Bcfg2.Client.Tools.Tool): __handles__ = [('ConfigFile', None), ('Directory', None), ('Path', 'ConfigFile'), - ('Path', 'Device'), + ('Path', 'device'), ('Path', 'Directory'), ('Path', 'HardLink'), ('Path', 'Perms'), @@ -140,10 +147,11 @@ class POSIX(Bcfg2.Client.Tools.Tool): fmode = os.lstat(entry.get('name'))[ST_MODE] if S_ISREG(fmode) or S_ISLNK(fmode): self.logger.debug("Non-directory entry already exists at " - "%s. Unlinking entry." % (entry.get('name'))) + "%s. Unlinking entry." % \ + (entry.get('name'))) os.unlink(entry.get('name')) elif S_ISDIR(fmode): - self.logger.debug("Directory entry already exists at %s" % \ + self.logger.debug("Directory entry already exists at %s" %\ (entry.get('name'))) self.cmd.run("mv %s/ %s.bak" % \ (entry.get('name'), @@ -158,12 +166,6 @@ class POSIX(Bcfg2.Client.Tools.Tool): except OSError: return False - def VerifyDevice(self, entry, _): - return False - - def InstallDevice(self, entry): - return False - def VerifyDirectory(self, entry, modlist): '''Verify Directory Entry''' while len(entry.get('perms', '')) < 4: @@ -254,7 +256,6 @@ class POSIX(Bcfg2.Client.Tools.Tool): entry.set('qtext', nnqtext) return pTrue and pruneTrue - def InstallDirectory(self, entry): '''Install Directory Entry''' self.logger.info("Installing Directory %s" % (entry.get('name'))) @@ -382,6 +383,82 @@ class POSIX(Bcfg2.Client.Tools.Tool): (entry.get('name'))) return False + def Verifydevice(self, entry, _): + '''Verify device entry''' + try: + # check for file existence + filestat = os.stat(entry.get('name')) + except OSError: + entry.set('current_exists', 'false') + self.logger.debug("%s %s does not exist" % + (entry.tag, entry.get('name'))) + return False + + try: + # attempt to verify device properties as specified in config + dev_type = entry.get('dev_type') + mode = calcPerms(device_map[dev_type], + entry.get('mode', '0600')) + owner = entry.get('owner') + group = entry.get('group') + if dev_type in ['block', 'char']: + major = int(entry.get('major')) + minor = int(entry.get('minor')) + if major == os.major(filestat.st_rdev) and \ + minor == os.minor(filestat.st_rdev) and \ + mode == filestat.st_mode and \ + owner == filestat.st_uid and \ + group == filestat.st_gid: + return True + else: + return False + elif dev_type == 'fifo' and \ + mode == filestat.st_mode and \ + owner == filestat.st_uid and \ + group == filestat.st_gid: + return True + else: + self.logger.info('Device properties for %s incorrect' % \ + entry.get('name')) + return False + except OSError: + self.logger.debug("%s %s failed to verify" % + (entry.tag, entry.get('name'))) + return False + + def Installdevice(self, entry): + '''Install device entries''' + try: + # check for existing paths and remove them + filestat = os.lstat(entry.get('name')) + try: + os.unlink(entry.get('name')) + exists = False + except OSError: + self.logger.info('Failed to unlink %s' % \ + entry.get('name')) + return False + except OSError: + exists = False + + if not exists: + try: + dev_type = entry.get('dev_type') + mode = calcPerms(device_map[dev_type], + entry.get('mode', '0600')) + if dev_type in ['block', 'char']: + major = int(entry.get('major')) + minor = int(entry.get('minor')) + device = os.makedev(major, minor) + os.mknod(entry.get('name'), mode, device) + else: + os.mknod(entry.get('name'), mode) + os.chown(entry.get('name'), normUid(entry), normGid(entry)) + return True + except OSError: + self.logger.error('Failed to install %s' % entry.get('name')) + return False + def Verifynonexistent(self, entry, _): '''Verify nonexistent entry''' # return true if path does _not_ exist diff --git a/src/lib/Server/Plugins/POSIXCompat.py b/src/lib/Server/Plugins/POSIXCompat.py index 00fdbf65c..d2e530407 100644 --- a/src/lib/Server/Plugins/POSIXCompat.py +++ b/src/lib/Server/Plugins/POSIXCompat.py @@ -8,11 +8,12 @@ import Bcfg2.Server.Plugin # FIXME: We will need this mapping if we decide to change the # specification to use lowercase types for new POSIX entry types -#COMPAT_DICT = {'configfile': 'ConfigFile', -# 'device': 'Device', -# 'directory': 'Directory', -# 'permissions': 'Permissions', -# 'symlink': 'SymLink'} +COMPAT_DICT = {'configfile': 'ConfigFile', + 'device': 'device', + 'directory': 'Directory', + 'nonexistent': 'nonexistent', + 'permissions': 'Permissions', + 'symlink': 'SymLink'} class POSIXCompat(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.GoalValidator): @@ -27,9 +28,10 @@ class POSIXCompat(Bcfg2.Server.Plugin.Plugin, def validate_goals(self, metadata, goals): for goal in goals: for entry in goal.getchildren(): - if entry.tag == 'Path' and entry.get('type') != 'nonexistent': + if entry.tag == 'Path' and \ + entry.get('type') not in ['nonexistent', 'device']: oldentry = entry - entry.tag = entry.get('type') + entry.tag = COMPAT_DICT[entry.get('type')] del entry.attrib['type'] # FIXME: use another attribute? old clients only # know about type=None -- cgit v1.2.3-1-g7c22