X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-console.git/blobdiff_plain/5a9b61b747ead3307eabd89e2c091fe8a31f4831..281bc8b42eeeb6b395c3430bd603b7dad59218ff:/files/usr/bin/sipb-xen-consolefs?ds=sidebyside

diff --git a/files/usr/bin/sipb-xen-consolefs b/files/usr/bin/sipb-xen-consolefs
index 380c94b..d5187c5 100755
--- a/files/usr/bin/sipb-xen-consolefs
+++ b/files/usr/bin/sipb-xen-consolefs
@@ -10,19 +10,14 @@ import os	  # for filesystem modes (O_RDONLY, etc)
 import errno   # for error number codes (ENOENT, etc)
 			   # - note: these must be returned as negatives
 
+from syslog import *
+
 import sipb_xen_database
 
 fuse.fuse_python_api = (0, 2)
 
 realpath = "/home/machines/"
 
-def dirFromList(list):
-	"""
-	Return a properly formatted list of items suitable to a directory listing.
-	['a', 'b', 'c'] => [('a', 0), ('b', 0), ('c', 0)]
-	"""
-	return [(x, 0) for x in list]
-
 def getDepth(path):
 	"""
 	Return the depth of a given path, zero-based from root ('/')
@@ -39,6 +34,8 @@ def getParts(path):
 	if path == '/':
 		return ['/']
 	else:
+		# [1:] because otherwise you get an empty list element from the
+		# initial '/'
 		return path[1:].split('/')
 
 class MyStat:
@@ -59,39 +56,61 @@ class MyStat:
 
 class ConsoleFS(Fuse):
 	"""
+	ConsoleFS creates a series of subdirectories each mirroring the same real
+	directory, except for a single file - the .k5login - which is dynamically
+	generated for each subdirectory
+	
+	This filesystem only implements the getattr, getdir, read, and readlink
+	calls, beacuse this is a read-only filesystem
 	"""
 	
 	def __init__(self, *args, **kw):
+		"""Initialize the filesystem and set it to allow_other access besides
+		the user who mounts the filesystem (i.e. root)
+		"""
 		Fuse.__init__(self, *args, **kw)
 		self.lasttime = time()
 		self.allow_other = 1
-		print 'Init complete.'
+		
+		openlog('sipb-xen-consolefs ', LOG_PID, LOG_DAEMON)
+		
+		syslog(LOG_DEBUG, 'Init complete.')
 	
 	def mirrorPath(self, path):
+		"""Translate a virtual path to its real path counterpart"""
 		return realpath + "/".join(getParts(path)[1:])
 	
 	def getMachines(self):
+		"""Get the list of VMs in the database, clearing the cache if it's 
+		older than 15 seconds"""
 		if time() - self.lasttime > 15:
 			self.lasttime = time()
 			sipb_xen_database.clear_cache()
 		return [machine.name for machine in sipb_xen_database.Machine.select()]
 	
 	def getUid(self, machine_name):
+		"""Calculate the UID of a machine-account, which is just machine_id+1000
+		"""
 		return sipb_xen_database.Machine.get_by(name=machine_name).machine_id + 1000
 	
 	def getK5login(self, machine_name):
+		"""Build the ACL for a machine and turn it into a .k5login file
+		"""
 		machine = sipb_xen_database.Machine.get_by(name=machine_name)
 		users = [acl.user for acl in machine.acl]
 		return "\n".join(map(self.userToPrinc, users) + [''])
 	
 	def userToPrinc(self, user):
+		"""Convert Kerberos v4-style names to v5-style and append a default
+		realm if none is specified
+		"""
 		if '@' in user:
 			(princ, realm) = user.split('@')
 		else:
 			princ = user
 			realm = "ATHENA.MIT.EDU"
 		
-		return princ.replace('.', '/') + realm
+		return princ.replace('.', '/') + '@' + realm
 	
 	def getattr(self, path):
 		"""
@@ -108,60 +127,98 @@ class ConsoleFS(Fuse):
 					or the time of creation on Windows).
 		"""
 		
-		print "*** getattr: " + path
+		syslog(LOG_DEBUG, "*** getattr: " + path)
 		
 		depth = getDepth(path)
 		parts = getParts(path)
 		
 		st = MyStat()
+		# / is a directory
 		if path == '/':
 			st.st_mode = stat.S_IFDIR | 0755
 			st.st_nlink = 2
+		# /foo is a directory if foo is a machine - otherwise it doesn't exist
 		elif depth == 1:
 			if parts[-1] in self.getMachines():
 				st.st_mode = stat.S_IFDIR | 0755
 				st.st_nlink = 2
+				# Homedirs should be owned by the user whose homedir it is
 				st.st_uid = st.st_gid = self.getUid(parts[0])
 			else:
 				return -errno.ENOENT
+		# Catch the .k5login file, because it's a special case
 		elif depth == 2 and parts[-1] == '.k5login':
 			st.st_mode = stat.S_IFREG | 0444
 			st.st_nlink = 1
 			st.st_size = len(self.getK5login(parts[0]))
+			# The .k5login file should be owned by the user whose homedir it is
 			st.st_uid = st.st_gid = self.getUid(parts[0])
+		# For anything else, we get the mirror path and call out to the OS
 		else:
 			stats = list(os.lstat(self.mirrorPath(path)))
+			# Shadow the UID and GID from the original homedir
 			stats[4:6] = [self.getUid(parts[0])] * 2
 			return tuple(stats)
 		return st.toTuple()
 	
+	# This call isn't actually used in the version of Fuse on console, but we
+	# wanted to leave it implemented to ease the transition in the future
 	def readdir(self, path, offset):
-		print '*** readdir', path, offset
+		"""Return a generator with the listing for a directory
+		"""
+		syslog(LOG_DEBUG, '*** readdir %s %s' % (path, offset))
 		for (value, zero) in self.getdir(path):
 			yield fuse.Direntry(value)
 	
 	def getdir(self, path):
-		print '*** getdir', path
+		"""Return a list of tuples of the form (item, 0) with the contents of
+		the directory path
+		
+		Fuse doesn't add '.' or '..' on its own, so we have to
+		"""
+		syslog(LOG_DEBUG, '*** getdir %s' % path)
+		
+		# '/' contains a directory for each machine
 		if path == '/':
-			contents = ['.', '..']+self.getMachines()
+			contents = self.getMachines()
+		# The directory for each machine contains the same files as the realpath
+		# but also the .k5login
+		#
+		# The list is converted to a set so that we can handle the case where 
+		# there is already a .k5login in the realpath gracefully
 		elif getDepth(path) == 1:
-			contents = set(os.listdir(self.mirrorPath(path)) + ['.k5login', '.', '..'])
+			contents = set(os.listdir(self.mirrorPath(path)) + ['.k5login'])
+		# If it's not the root of the homedir, just pass the call onto the OS
+		# for realpath
 		else:
-			contents = os.listdir(self.mirrorPath(path)) + ['.', '..']
-		return [(i, 0) for i in contents]
+			contents = os.listdir(self.mirrorPath(path))
+		# Format the list the way that Fuse wants it - and don't forget to add
+		# '.' and '..'
+		return [(i, 0) for i in (list(contents) + ['.', '..'])]
 	
-	def read ( self, path, length, offset ):
-		print '*** read', path, length, offset
+	def read(self, path, length, offset):
+		"""Read length bytes starting at offset of path. In most cases, this
+		just gets passed on to the OS
+		"""
+		syslog(LOG_DEBUG, '*** read %s %s %s' % (path, length, offset))
 		
 		parts = getParts(path)
 		
+		# If the depth is less than 2, then either it's a directory or the file
+		# doesn't exist
+		# (realistically this doesn't appear to ever happen)
 		if getDepth(path) < 2:
 			return -errno.ENOENT
+		# If we're asking for a real .k5login file, then create it and return
+		# the snippet requested
 		elif parts[1:] == ['.k5login']:
 			if parts[0] not in self.getMachines():
 				return -errno.ENOENT
 			else:
 				return self.getK5login(parts[0])[offset:length + offset]
+		# Otherwise, pass the call onto the OS
+		# (note that the file will get closed when this call returns and the
+		# file descriptor goes out of scope)
 		else:
 			fname = self.mirrorPath(path)
 			if not os.path.isfile(fname):
@@ -170,6 +227,16 @@ class ConsoleFS(Fuse):
 				f = open(fname)
 				f.seek(offset)
 				return f.read(length)
+	
+	def readlink(self, path):
+		syslog(LOG_DEBUG, '*** readlink %s' % path)
+		
+		# There aren't any symlinks here
+		if getDepth(path) < 2:
+			return -errno.ENOENT
+		# But there might be here
+		else:
+			return os.readlink(self.mirrorPath(path))
 
 if __name__ == '__main__':
 	sipb_xen_database.connect('postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen')