Chris PeBenito 95db42
#!/usr/bin/python
Chris PeBenito 95db42
Chris PeBenito 95db42
#  Author: Donald Miner <dminer@tresys.com>
Chris PeBenito 95db42
#
Chris PeBenito 95db42
# Copyright (C) 2003 - 2005 Tresys Technology, LLC
Chris PeBenito 95db42
#      This program is free software; you can redistribute it and/or modify
Chris PeBenito 95db42
#      it under the terms of the GNU General Public License as published by
Chris PeBenito 95db42
#      the Free Software Foundation, version 2.
Chris PeBenito 95db42
Chris PeBenito 95db42
"""
Chris PeBenito 95db42
	This script generates XML documentation information for layers specified
Chris PeBenito 95db42
	by the user.
Chris PeBenito 95db42
"""
Chris PeBenito 95db42
Chris PeBenito 95db42
import sys
Chris PeBenito 95db42
import os
Chris PeBenito 95db42
import glob
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# GLOBALS
Chris PeBenito 95db42
class dec_style:
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	"Declaration Style"
Chris PeBenito 95db42
	Specifies the syntax of a declaration. Intended to be used with
Chris PeBenito 95db42
	getParams().
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Example of a line: foo(bar,one,two);
Chris PeBenito 95db42
	# A style that would fit this: dec_style("foo(",3,",",");")
Chris PeBenito 95db42
	#  "foo(" - the opening of it, ends at the begining of the first param.
Chris PeBenito 95db42
	#  3 - the number of parameters.
Chris PeBenito 95db42
	#  "," - the delimeter to parse apart parameters.
Chris PeBenito 95db42
	#  ");" - the end of the declaration statement.
Chris PeBenito 95db42
Chris PeBenito 95db42
	def __init__(self,open_str,params,delim,close_str):
Chris PeBenito 95db42
		self.open_str = open_str
Chris PeBenito 95db42
		self.params = params
Chris PeBenito 95db42
		self.delim = delim
Chris PeBenito 95db42
		self.close_str = close_str
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
INTERFACE = dec_style("interface(`",1,None,"'")
Chris PeBenito e81f02
TEMPLATE = dec_style("template(`",1,None,"'")
Chris PeBenito 95db42
TUNABLE = dec_style("gen_tunable(",2,",",")")
Chris PeBenito 95db42
# boolean FIXME: may have to change in the future.
Chris PeBenito 95db42
BOOLEAN = dec_style("gen_bool(",2,",",")")
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# Default values of command line arguments.
Chris PeBenito 95db42
directory = "./"
Chris PeBenito 95db42
warn = False
Chris PeBenito 95db42
meta = "metadata"
Chris PeBenito 95db42
layers = []
Chris PeBenito 95db42
tunable_files = []
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# FUNCTIONS
Chris PeBenito 95db42
def getXMLComment(line):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Returns the XML comment, (removes "## " from the front of the line).
Chris PeBenito 95db42
	Returns False if the line is not an XML comment.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	for i in range(0,len(line)-1):
Chris PeBenito 95db42
		# Check if the first 3 characters are "## "
Chris PeBenito 95db42
		if line[i:i+3] in ("## ", "##\t"):
Chris PeBenito 95db42
			# The chars before '#' in the line must be whitespace.
Chris PeBenito 95db42
			if i > 0 and not line[0:i-1].isspace():
Chris PeBenito 95db42
				return False
Chris PeBenito 95db42
			else:
Chris PeBenito 95db42
				return line[i+3:]
Chris PeBenito 95db42
Chris PeBenito 95db42
	# No XML comment.
Chris PeBenito 95db42
	return False	
Chris PeBenito 95db42
Chris PeBenito 95db42
def getParams(line, style):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Returns a list of items, containing the values of each parameter.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Clean out whitespace.
Chris PeBenito 95db42
	temp_line = line.strip()
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Check to see if the string begins with the specified opening
Chris PeBenito 95db42
	# string specified by style.
Chris PeBenito 95db42
	if temp_line[0:len(style.open_str)] == style.open_str:
Chris PeBenito 95db42
		temp_line = temp_line[len(style.open_str):].strip()
Chris PeBenito 95db42
	else:
Chris PeBenito 95db42
		return False
Chris PeBenito 95db42
Chris PeBenito 95db42
	# If there is a delimeter.
Chris PeBenito 95db42
	if style.delim:
Chris PeBenito 95db42
		temp_line = temp_line.split(style.delim)
Chris PeBenito 95db42
	else:
Chris PeBenito 95db42
		temp_line = [temp_line]
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Only interested in a sertain number of tokens, specified by style.
Chris PeBenito 95db42
	temp_line = temp_line[:style.params]
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Remove the end of the declaration, specified by style.
Chris PeBenito e81f02
	end = temp_line[-1].find(style.close_str)
Chris PeBenito 95db42
	if end == -1:
Chris PeBenito 95db42
		warning("line \"%s\" may be syntactically incorrect"\
Chris PeBenito 95db42
			% line.strip())
Chris PeBenito 95db42
		return False
Chris PeBenito 95db42
Chris PeBenito e81f02
	temp_line[-1] = temp_line[-1][:end]
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Remove whitespace
Chris PeBenito 95db42
	for i in range(0,len(temp_line)-1):
Chris PeBenito 95db42
		temp_line[i] = temp_line[i].strip()
Chris PeBenito 95db42
Chris PeBenito 95db42
	return temp_line
Chris PeBenito 95db42
Chris PeBenito 95db42
def getModuleXML(file_name):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Returns the XML data for a module in a list, one line per list item.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Try to open the file, if it cant, just ignore it.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		module_file = open(file_name, "r")
Chris PeBenito 95db42
		module_code = module_file.readlines()
Chris PeBenito 95db42
		module_file.close()
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot open file %s for read, skipping" % file_name)
Chris PeBenito 95db42
		return []
Chris PeBenito 95db42
Chris PeBenito 95db42
	module_buf = []
Chris PeBenito 95db42
Chris PeBenito e81f02
	# Infer the module name, which is the base of the file name.
Chris PeBenito e81f02
	module_buf.append("<module name=\"%s\">\n" 
Chris PeBenito e81f02
		% os.path.splitext(os.path.split(file_name)[-1])[0])
Chris PeBenito 95db42
Chris PeBenito 95db42
	temp_buf = []
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Phases:	find header - looking for the header of the file.
Chris PeBenito 95db42
	#		get header - get the header comments and stop when first
Chris PeBenito 95db42
	#			     whitespace is encountered.
Chris PeBenito 95db42
	#		find interface - looking for interfaces to get info for.
Chris PeBenito 95db42
	phase = "find header"
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Go line by line and figure out what to do with it.
Chris PeBenito 95db42
	for line in module_code:
Chris PeBenito 95db42
		# In this phase, whitespace and stray code is ignored at the
Chris PeBenito e81f02
		# top of the file.
Chris PeBenito 95db42
		if phase == "find header":
Chris PeBenito 95db42
			if line.isspace():
Chris PeBenito 95db42
				continue
Chris PeBenito 95db42
			# Once a comment is encountered, start trying to get the
Chris PeBenito 95db42
			# header documentation.
Chris PeBenito 95db42
			elif getXMLComment(line):
Chris PeBenito 95db42
				phase = "get header"
Chris PeBenito 95db42
			# If an interface is found, there is no header, and no
Chris PeBenito 95db42
			# documentation for the interface.
Chris PeBenito e81f02
			elif getParams(line,INTERFACE)\
Chris PeBenito e81f02
				 or getParams(line,TEMPLATE):
Chris PeBenito 95db42
				phase = "find interface"
Chris PeBenito 95db42
Chris PeBenito 95db42
		# In this phase, XML comments are being retrieved for the file.
Chris PeBenito 95db42
		if phase == "get header":
Chris PeBenito 95db42
			if getXMLComment(line):
Chris PeBenito 95db42
				temp_buf.append(getXMLComment(line))
Chris PeBenito 95db42
				continue
Chris PeBenito 95db42
			# If the line is whitespace, the file header is over,
Chris PeBenito 95db42
			# continue on to find interfaces.
Chris PeBenito 95db42
			elif line.isspace():
Chris PeBenito 95db42
				module_buf += temp_buf
Chris PeBenito 95db42
				temp_buf = []
Chris PeBenito 95db42
				phase = "find interface"
Chris PeBenito 95db42
				continue
Chris PeBenito 95db42
			# Oops! The comments we have been getting weren't part
Chris PeBenito 95db42
			# of the header so attribute them to an interface
Chris PeBenito 95db42
			# instead.
Chris PeBenito e81f02
			elif getParams(line,INTERFACE)\
Chris PeBenito e81f02
				 or getParams(line,TEMPLATE):
Chris PeBenito 95db42
				phase = "find interface"
Chris PeBenito 95db42
Chris PeBenito 95db42
		# In this phase, XML comments are being attributed
Chris PeBenito 95db42
		if phase == "find interface":
Chris PeBenito 95db42
			if getXMLComment(line):
Chris PeBenito 95db42
				temp_buf.append(getXMLComment(line))
Chris PeBenito 95db42
				continue
Chris PeBenito 95db42
			# If the line is the declaration of a interface,
Chris PeBenito 95db42
			# infer the interface name and add all the comments
Chris PeBenito 95db42
			# to the main buffer.
Chris PeBenito 95db42
			elif getParams(line,INTERFACE):
Chris PeBenito 95db42
				module_buf.append("<interface name=\"%s\">\n"\
Chris PeBenito 95db42
					% getParams(line,INTERFACE)[0])
Chris PeBenito e81f02
				if len(temp_buf):
Chris PeBenito e81f02
					module_buf += temp_buf
Chris PeBenito e81f02
				else:
Chris PeBenito f8838e
					module_buf.append("<summary>\n")
Chris PeBenito f8838e
					module_buf.append("Summary is unspecified.\n")
Chris PeBenito f8838e
					module_buf.append("</summary>\n")
Chris PeBenito e81f02
					module_buf.append("<param name=\"?\">\n")
Chris PeBenito f8838e
					module_buf.append("Parameters are unspecified.\n")
Chris PeBenito e81f02
					module_buf.append("</param>\n")
Chris PeBenito 95db42
				temp_buf = []
Chris PeBenito 95db42
				module_buf.append("</interface>\n")
Chris PeBenito 95db42
				continue
Chris PeBenito e81f02
			elif getParams(line,TEMPLATE):
Chris PeBenito e81f02
				module_buf.append("<template name =\"%s\">\n"\
Chris PeBenito e81f02
					% getParams(line,TEMPLATE)[0])
Chris PeBenito f8838e
				if len(temp_buf):
Chris PeBenito f8838e
					module_buf += temp_buf
Chris PeBenito f8838e
				else:
Chris PeBenito f8838e
					module_buf.append("<summary>\n")
Chris PeBenito f8838e
					module_buf.append("Summary is unspecified.\n")
Chris PeBenito f8838e
					module_buf.append("</summary>\n")
Chris PeBenito f8838e
					module_buf.append("<param name=\"?\">\n")
Chris PeBenito f8838e
					module_buf.append("Parameters unspecified.\n")
Chris PeBenito f8838e
					module_buf.append("</param>\n")
Chris PeBenito e81f02
				temp_buf = []
Chris PeBenito e81f02
				module_buf.append("</template>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	# If there are XML comments at the end of the file, they arn't
Chris PeBenito 95db42
	# attributed to anything. These are ignored.
Chris PeBenito 95db42
	if len(temp_buf):
Chris PeBenito 95db42
		warning("orphan XML comments at bottom of file %s" % file_name)
Chris PeBenito 95db42
		
Chris PeBenito 95db42
	module_buf.append("</module>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	return module_buf
Chris PeBenito 95db42
Chris PeBenito 95db42
def getLayerXML(directory):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Returns the XML documentation for a layer.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	layer_buf = []
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Infer the layer name from the directory name.
Chris PeBenito 95db42
	layer_buf.append("<layer name=\"%s\">\n" % os.path.basename(directory))
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Try to open the metadata file for this directory and if it exists,
Chris PeBenito 95db42
	# append the contents to the buffer.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		layer_meta = open(directory+"/"+meta, "r")
Chris PeBenito 95db42
		layer_buf += layer_meta.readlines()
Chris PeBenito 95db42
		layer_meta.close()
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot open file %s for read, assuming no data"\
Chris PeBenito 95db42
			% meta)
Chris PeBenito 95db42
Chris PeBenito 95db42
	# For each module file in the layer, add its XML.
Chris PeBenito 95db42
	for module in glob.glob("%s/*.if" % directory):
Chris PeBenito 95db42
		layer_buf += getModuleXML(module)
Chris PeBenito 95db42
Chris PeBenito 95db42
	layer_buf.append("</layer>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	return layer_buf
Chris PeBenito 95db42
Chris PeBenito 95db42
def getTunableXML(file_name):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Return all the XML for the tunables in the file specified.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Try to open the file, if it cant, just ignore it.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		tunable_file = open(file_name, "r")
Chris PeBenito 95db42
		tunable_code = tunable_file.readlines()
Chris PeBenito 95db42
		tunable_file.close()
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot open file %s for read, skipping" % file_name)
Chris PeBenito 95db42
		return []
Chris PeBenito 95db42
Chris PeBenito 95db42
	tunable_buf = []
Chris PeBenito 95db42
	temp_buf = []
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Find tunables and booleans line by line and use the comments above
Chris PeBenito 95db42
	# them.
Chris PeBenito 95db42
	for line in tunable_code:
Chris PeBenito 95db42
		# If it is an XML comment, add it to the buffer and go on.
Chris PeBenito 95db42
		if getXMLComment(line):
Chris PeBenito 95db42
			temp_buf.append(getXMLComment(line))
Chris PeBenito 95db42
			continue
Chris PeBenito 95db42
Chris PeBenito 95db42
		# Get the parameters of a TUNABLE style line.
Chris PeBenito 95db42
		params = getParams(line,TUNABLE)
Chris PeBenito 95db42
Chris PeBenito 95db42
		# If the line is not a TUNABLE style declaration, try BOOLEAN.
Chris PeBenito 95db42
		if not params:
Chris PeBenito 95db42
			params = getParams(line,BOOLEAN)
Chris PeBenito 95db42
Chris PeBenito 95db42
		# If the line is one of the two styles above, add a tunable tag
Chris PeBenito 95db42
		# and give it the data from the temprorary buffer.
Chris PeBenito 95db42
		if params:
Chris PeBenito 95db42
			tunable_buf.append\
Chris PeBenito c3a075
				("<tunable name=\"%s\" dftval=\"%s\">\n"
Chris PeBenito 95db42
				% (params[0], params[1]))
Chris PeBenito 95db42
			tunable_buf += temp_buf
Chris PeBenito 95db42
			temp_buf = []
Chris PeBenito c3a075
			tunable_buf.append("</tunable>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	# If there are XML comments at the end of the file, they arn't
Chris PeBenito 95db42
	# attributed to anything. These are ignored.
Chris PeBenito 95db42
	if len(temp_buf):
Chris PeBenito 95db42
		warning("orphan XML comments at bottom of file %s" % file_name)
Chris PeBenito 95db42
Chris PeBenito 95db42
	return tunable_buf
Chris PeBenito 95db42
Chris PeBenito 95db42
def getPolicyXML(directory):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Return the compelete reference policy XML documentation through a list,
Chris PeBenito 95db42
	one line per item.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Keep track of original path so that it will change back at the end.
Chris PeBenito 95db42
	old_dir = os.path.abspath(os.path.curdir)
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Attempt to change directory into the policy directory. If it doesn't
Chris PeBenito 95db42
	# exist just return an empty documentation.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		os.chdir(directory)
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot change directory to %s, ignoring"\
Chris PeBenito 95db42
			% directory)
Chris PeBenito 95db42
		return []
Chris PeBenito 95db42
Chris PeBenito 95db42
	policy_buf = []
Chris PeBenito 95db42
	policy_buf.append("<policy>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Add to the XML each layer specified by the user.
Chris PeBenito 95db42
	for layer in layers:
Chris PeBenito 95db42
		policy_buf += getLayerXML(layer)
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Add to the XML each tunable specified by the user.
Chris PeBenito 95db42
	for tunable_file in tunable_files:
Chris PeBenito 95db42
		policy_buf += getTunableXML(tunable_file)
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
	policy_buf.append("</policy>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Return to old directory.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		os.chdir(old_dir)
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		error("cannot change directory to %s" % old_dir)
Chris PeBenito 95db42
Chris PeBenito 95db42
	return policy_buf
Chris PeBenito 95db42
Chris PeBenito 95db42
def usage():
Chris PeBenito 95db42
	"""
Chris PeBenito 95db42
	Displays a message describing the proper usage of this script.
Chris PeBenito 95db42
	"""
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("usage: %s [-w] [-d directory] [-m file] "\
Chris PeBenito 95db42
		% sys.argv[0])
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("layerdirectory [layerdirectory...]\n\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("Options:\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-w --warn		--	"+\
Chris PeBenito 95db42
				"show warnings\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-m --meta <file>	--	"+\
Chris PeBenito 95db42
				"the filename of the metadata in each layer\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-d --directory <dir>	--	"+\
Chris PeBenito 95db42
				"directory where the layers are\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-t --tunable <file>	--	"+\
Chris PeBenito 95db42
				"A file containing tunable declarations\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
def warning(description):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Warns the user of a non-critical error.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	if warn:
Chris PeBenito 95db42
		sys.stderr.write("%s: " % sys.argv[0] )
Chris PeBenito 95db42
		sys.stderr.write("warning: " + description + "\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
def error(description):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Describes an error and exists the program.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stderr.write("%s: " % sys.argv[0] )
Chris PeBenito 95db42
        sys.stderr.write("error: " + description + "\n")
Chris PeBenito 95db42
        sys.stderr.flush()
Chris PeBenito 95db42
        sys.exit(1)
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# MAIN PROGRAM
Chris PeBenito 95db42
# Check that there are command line arguments.
Chris PeBenito 95db42
if len(sys.argv) <= 1:
Chris PeBenito 95db42
	usage()
Chris PeBenito 95db42
	sys.exit(1)
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# Parse the command line arguments
Chris PeBenito 95db42
for i in range(1, len(sys.argv)):
Chris PeBenito 95db42
	if sys.argv[i-1] in ("-d", "--directory", "-m", "--meta",\
Chris PeBenito 95db42
					"-t", "--tunable"):
Chris PeBenito 95db42
		continue
Chris PeBenito 95db42
	elif sys.argv[i] in ("-w", "--warn"):
Chris PeBenito 95db42
		warn = True
Chris PeBenito 95db42
	elif sys.argv[i] in ("-d", "--directory"):
Chris PeBenito 95db42
		if i < len(sys.argv)-1:
Chris PeBenito 95db42
			directory = sys.argv[i+1]
Chris PeBenito 95db42
		else:
Chris PeBenito 95db42
			usage()
Chris PeBenito 95db42
	elif sys.argv[i] in ("-m", "--meta"):
Chris PeBenito 95db42
		if i < len(sys.argv)-1:
Chris PeBenito 95db42
			meta = sys.argv[i+1]
Chris PeBenito 95db42
		else:
Chris PeBenito 95db42
			usage()
Chris PeBenito 95db42
	elif sys.argv[i] in ("-t", "--tunable"):
Chris PeBenito 95db42
		if i < len(sys.argv)-1:
Chris PeBenito 95db42
			tunable_files.append(sys.argv[i+1])
Chris PeBenito 95db42
		else:
Chris PeBenito 95db42
			usage()
Chris PeBenito 95db42
	else:
Chris PeBenito 95db42
		layers.append(sys.argv[i])
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# Generate the XML and output it to a file
Chris PeBenito 95db42
lines = getPolicyXML(directory)
Chris PeBenito 95db42
for s in lines:
Chris PeBenito 95db42
	sys.stdout.write(s)