#!/usr/bin/python
# PYTHON_ARGCOMPLETE_OK
#
# Copyright 2013,2014,2015,2016,2017,2018,2019,2020 Cumulus Networks, Inc.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
# cl-acltool --
#    tool to install cumulus acl policy/rules into kernel and hw
#
try:
	import argcomplete
	import argparse
	import os
	import shutil
	import subprocess
	import sys
	import syslog
	import time
	import glob
	import errno
	from cumulus.aclpolicy import *
except ImportError, e:
	raise ImportError (str(e) + "- required module not found")

ctrl_base = "/cumulus/switchd"
policy_dir_default='/etc/cumulus/acl/policy.d'
policy_conf_name='policy.conf'
policy_conf='/etc/cumulus/acl/' + policy_conf_name
policy_files=[]
env_vars={}
scratchdir = '/tmp/.acl' + '.%d' %os.getpid() + '/'
cachedir='/run/cache/cumulus/acltool/'
varrundir='/var/run/acl/'
varrundir_lastgoodpolicyfiles=varrundir + 'lastgoodpolicyfiles'
donefile=cachedir + 'installed'
config = {}
acl_handlers = {}
debug = False
verbose = False
quiet = False
numeric = False
exact = False
version='1.0'
hw_sync=True
hw_wait=0
log_method=""
log_handler=None
pvt_dot1x_mode = False

syslog_priority_map = { "crit" : syslog.LOG_CRIT,
			"error" : syslog.LOG_ERR,
			"info" : syslog.LOG_INFO,
			"warn" : syslog.LOG_WARNING,
			"debug" : syslog.LOG_DEBUG }

stdout_priority_map = { "crit" : "error",
			"error" : "error",
			"info" : "",
			"warn" : "warning",
			"debug" : "debug" }

def log_handler_stdout(priority, buf):
	p = stdout_priority_map.get(priority, "")
	if p != "":
		p = p + ': '
	print p + buf
	sys.stdout.flush()

def log_handler_stderr(priority, buf):
	p = stdout_priority_map.get(priority, "")
	if p != "":
		p = p + ': '
	sys.stderr.write(p + buf)
	sys.stderr.flush()

def log_handler_syslog(priority, buf):
	syslog.syslog(syslog_priority_map.get(priority, syslog.LOG_INFO), buf)

def log_msg_begin(*args, **kwargs):
	if quiet == False:
		log_handler('', ''.join(args))

def log_msg_end(ret):
	if quiet == False:
		if ret == 0:
			log_handler('', 'done.')
		else:
			log_handler('', 'failed.')

def log(*args, **kwargs):
	if quiet == False:
		log_handler("info", ''.join(args))

def log_debug(*args, **kwargs):
	if debug == True:
		log_handler("debug", ''.join(args))

def log_verbose(*args, **kargs):
	if verbose == True:
		log_handler("", ''.join(args))

def log_error(*args, **kwargs):
	if log_method == "syslog":
		log_handler("error", ''.join(args))
	else:
		log_handler_stderr("error", ''.join(args))

def log_warn(*args, **kwargs):
	log_handler("warn", ''.join(args))

def log_crit(*args, **kwargs):
	log_handler("crit", ''.join(args))

def log_cmderr(cmd, cmdout):
	log_error('cmd \'%s\'' %cmd +
		' failed with the following error:\n' +
		'(' + cmdout.rstrip('\n') + ')')

def log_parseerr(filename, line, lineno, errstr=None):
    errmsg = '%s: %d: invalid line \'%s\'' %(filename, lineno, line)
    if errstr:
        errmsg += ' (%s)' %errstr
    log_error(errmsg)

def createdir(dirpath):
	mode = 0755
	try:
		log_debug('Creating dir ..' + dirpath)
		if not os.path.exists(dirpath):
			os.makedirs(dirpath, mode)
	except OSError, e:
		log_error('mkdir failed : ' + str(e))
		return -1

	return 0

def removedir(dirpath):
	try:
		log_debug('Removing dir ..' + dirpath)
		#os.removedirs(dirpath)
		shutil.rmtree(dirpath)
	except OSError, e:
		log_error('rmdir failed : ' + str(e))
		return -1

	return 0

def remove_file(filepath):
	try:
		os.remove(filepath)
	except OSError, e:
		pass

def cat_file(filepath):
	print '-' * 80
	cmd = ['cat', '-n', filepath]
	print ' '.join(cmd)
	subprocess.call(cmd)
	print '-' * 80
	print '\n'

def touch_file(filepath):
	try:
		log_verbose('touch file %s' %filepath)
		f = open(filepath, 'w')
		os.utime(filepath, None)
		f.close()
	except OSError, e:
		log_error('Could not touch file ' + filepath +
			'(' + str(e) + ')')
		return -1

	return 0

def exec_command(cmd, quiet):
	retval = 0
	cmd_returncode = 0
	cmdout = ''

	try:
		log_verbose('Executing ' + cmd)
		ch = subprocess.Popen(cmd.split(),
				stdout=subprocess.PIPE,
				shell=False, stderr=subprocess.STDOUT)
		cmdout = ch.communicate()[0]
		cmd_returncode = ch.wait()
		if cmd_returncode != 0:
			retval = -1

	except OSError, e:
		cmdout = 'Could not execute ' + cmd + '(' + str(e) + ')'
		retval = -1

	return (retval, cmdout)

def read_config_file(filename):
	global config
	fconf_lines = []

	log_verbose('Reading config file %s' %filename)

	try:
		fconf = open(filename, 'r')
		fconf_lines = fconf.readlines()
	except Exception as e:
		raise RuntimeError(str(e))

	if len(fconf_lines) == 0:
		return 0

	for l in fconf_lines:
		l = l.strip()
		if len(l) == 0:
			continue
		if l[0] == '#':
			continue;

		if re.search('^.*=.*', l, 0) != None:
			l = re.sub(r'\s+', '', l)
			l = l.rstrip('\n')
			key_val = string.split(l, '=', 1)
			if len(key_val[0]) > 0 and len(key_val[1]) > 0:
				config[key_val[0]] = key_val[1]

	if len(config) > 0:
		log_debug('Config file contents :')
		log_debug(str(config))

	return 0

def hw_init():
	global hw_wait

	while True:
		try:
			with open(ctrl_base + "/ctrl/acl/stop_stats_sync", "w") as f:
				return 0
		except IOError:
			if not hw_wait:
				log_error("failed to connect to hw")
				return -1
			time.sleep(1)
			hw_wait -= 1

def hw_sync_start():
	# note: hidden file
	with open(ctrl_base + "/ctrl/acl/stop_stats_sync", "w") as f:
		f.write("1")
	return 0

def hw_sync_cancel():
	# note: hidden file
	with open(ctrl_base + "/ctrl/acl/reset_stats_sync", "w") as f:
		f.write("1")
	return 0

def hw_sync_commit():
	# note: hidden file
	with open(ctrl_base + "/ctrl/acl/resync", "r+") as f:
		f.write("1")
		f.seek(0)
		ret = f.read().strip()
		if ret and len(ret) > 1:
			log_crit('hw sync failed (%s)' %ret)
			return -1
	return 0

def hw_sync_end():
	# nop
	return 0

def iptables_restore_all_tables(basefilename, type, extra_options, noflush):
	""" Invoke iptables restore for all tables.
	filename is the prefix for all filenames """

	err = 0
	for table in iptables.kernel_tables:
		filename = basefilename + '.' + table
		if os.path.exists(filename):
			if type == aclRuleType.iptables:
				cmd, ret, cmdout = iptables.restore(table,
						filename, int(noflush), extra_options, verbose)
			elif type == aclRuleType.ip6tables:
				cmd, ret, cmdout = ip6tables.restore(table,
						filename, int(noflush), extra_options, verbose)
			if ret != 0:
				log_cmderr(cmd, cmdout)
				cat_file(filename)
				err += 1

	if err != 0:
		return -1

	return 0

def ebtables_restore_all_tables(basefilename):
	err = 0
	for table in ebtables.kernel_tables:
		filename = basefilename + '.' + table
		if os.path.exists(filename):
			cmd, ret, cmdout = ebtables.restore(table, filename,
						'', verbose)
			if ret != 0:
				log_cmderr(cmd, cmdout)
				cat_file(filename)
				err += 1

	if err != 0:
		return -1

	return 0

def iptables_save_all_tables(basefilename, type, noflush):
	err = 0
	for table in iptables.kernel_tables:
		ret = 0
		filename = basefilename + '.' + table
		if type == aclRuleType.iptables:
			log_verbose('Calling iptables save for '
					'table %s' %table)
			cmd, ret, cmdout = iptables.save(table,
						filename, '-c',
						verbose)
		elif type == aclRuleType.ip6tables:
			log_verbose('Calling ip6tables save for '
					'table %s' %table)
			cmd, ret, cmdout = ip6tables.save(table,
						filename, '-c',
						verbose)
		if ret != 0:
			log_cmderr(cmd, cmdout)
			err += 1

	if err != 0:
		return -1

	return 0

def ebtables_save_all_tables(basefilename):
	err = 0
	for table in ebtables.kernel_tables:
		filename = basefilename + '.' + table
		log_verbose('Calling ebtables save for '
				'table %s' %table)
		cmd, ret, cmdout = ebtables.save(table, filename, '',
						verbose)
		if ret != 0:
			log_cmderr(cmd, cmdout)
			err += 1

	if err != 0:
		return -1

	return 0

#This function adjusts LOG rule where src and dest IPs are comma separated
#It goes over the rules and splits them as below
#if rule is     --> -A FORWARD -i swp1 -s 10.10.1.1/24,20.1.1.1 -d 30.1.1.1,40.40.1.2/24 -j LOG
#and sibling is --> -A FORWARD -i swp1 -s 10.10.1.1/24,20.1.1.1 -d 30.1.1.1,40.40.1.2/24 -j DROP
#Above will be split into 4 pair of rules as below
# -A FORWARD -i swp1 -s 10.10.1.1/24 -d 30.1.1.1 -j LOG
# -A FORWARD -i swp1 -s 10.10.1.1/24 -d 30.1.1.1 -j DROP
# -A FORWARD -i swp1 -s 10.10.1.1/24 -d 40.40.1.2/24 -j LOG
# -A FORWARD -i swp1 -s 10.10.1.1/24 -d 40.40.1.2/24 -j DROP
# -A FORWARD -i swp1 -s 20.1.1.1 -d 30.1.1.1 -j LOG
# -A FORWARD -i swp1 -s 20.1.1.1 -d 30.1.1.1 -j DROP
# -A FORWARD -i swp1 -s 20.1.1.1/24 -d 40.40.1.2/24 -j LOG
# -A FORWARD -i swp1 -s 20.1.1.1/24 -d 40.40.1.2/24 -j DROP
def iptables_log_rule_adjust(r, rule_strs, rstr_index, table, rule_bufs, rule_cnts, rule_pair_strs):
    #Only required for sibling rules
    if r.has_sibling() != 0:
        src_addr_present = False
        dst_addr_present = False
        num_src_addr = 0
        num_dst_addr = 0
        total_num_add_comb = 0
        split_src_addr = []
        split_dst_addr = []
        src_m_itr = 0
        dst_m_itr = 0
        split_rule_str = rule_strs[rstr_index].split()
        #print ('Split Rule Str --> %s\n'%(split_rule_str))
        cnt_rule_sstr = len(split_rule_str)
        #print ('Count Str : %d\n'%(cnt_rule_sstr))

        for m_itr in range(0,len(split_rule_str)):
            #Look for -s 
            if "-s" == split_rule_str[m_itr]:
                src_addr_present = True
                src_m_itr = m_itr
                #print('Src Addr Found at Index : %d | Address : %s\n'%(m_itr,split_rule_str[m_itr+1]))
                src_addr_str = split_rule_str[m_itr+1]
                #If source IP address is found, its a min of 1 Src IP address
                num_src_addr = 1
                #Number of src IP addresses is +1 of the number os commas
                num_src_addr += src_addr_str.count(',')
                #print('Num of Source Addresses : %d\n'%(num_src_addr))
                #split the source IP addresses on comma
                split_src_addr = src_addr_str.split(',')
                #remove an " if present
                for src_itr in range(0,len(split_src_addr)):
                    split_src_addr[src_itr] = split_src_addr[src_itr].replace("\"","")
                #print('Source Address Split str : %s\n'%(split_src_addr))

            #Look for -d
            if "-d" == split_rule_str[m_itr]:
                dst_addr_present = True
                dst_m_itr = m_itr
                #print('Dst Addr Found at Index : %d | Address : %s\n'%(m_itr,split_rule_str[m_itr+1]))
                dst_addr_str= split_rule_str[m_itr+1]
                #If dst IP address is found, its a min of 1 dst IP address
                num_dst_addr= 1
                #Num of dst IP addresses is +1 of the num of commas
                num_dst_addr += dst_addr_str.count(',')
                #print('Num of Destination Addresses : %d\n'%(num_dst_addr))
                #Split the dst IP add string on comma
                split_dst_addr = dst_addr_str.split(',')
                #replace all " from the addresses
                for dst_itr in range(0,len(split_dst_addr)):
                    split_dst_addr[dst_itr] = split_dst_addr[dst_itr].replace("\"","")
                #print('Destination Address Split str : %s\n'%(split_dst_addr))

        #If no Source or Dst IP Addresses are present
        if src_addr_present == False and dst_addr_present == False:
                return 0

        #If both source and dst IP addresses are present
        if num_src_addr !=0 and num_dst_addr != 0:
            #Total number is a cross of all the src and dst IP addresses
            total_num_add_comb = num_src_addr * num_dst_addr
            #Iterate over Src IP addresses
            for src_itr in range (0,num_src_addr):
                #Iterate over Dst IP addresses
                for dst_itr in range (0,num_dst_addr):
                    if rule_strs != None:
                        #Get the original rule string
                        rule_str = rule_strs[rstr_index]
                        tmp_rule_str = rule_str
                        #replace the src IP string(with commas) with the src IP address
                        tmp_rule_str = tmp_rule_str.replace(split_rule_str[src_m_itr+1],split_src_addr[src_itr])
                        #replace the dst IP string(with commas) with the dst IP address
                        tmp_rule_str = tmp_rule_str.replace(split_rule_str[dst_m_itr+1],split_dst_addr[dst_itr])
                        #print('New Rule 3 --> %s\n'%tmp_rule_str)
                        #Add the rules to the rules buf
                        rule_bufs[table].append(tmp_rule_str + '\n')
                        rule_cnts[table] += 1
                        if rule_pair_strs != None:
                            #Get the sibling rule with DROP option
                            rule_pair_str = rule_pair_strs[rstr_index]
                            tmp_rule_pair_str = rule_pair_str
                            #replace the src IP string(with commas) with the src IP address
                            tmp_rule_pair_str = tmp_rule_pair_str.replace(split_rule_str[src_m_itr+1],split_src_addr[src_itr])
                            #replace the dst IP string(with commas) with the dst IP address
                            tmp_rule_pair_str = tmp_rule_pair_str.replace(split_rule_str[dst_m_itr+1],split_dst_addr[dst_itr])
                            #print('New Rule 4 --> %s\n'%tmp_rule_pair_str)
                            #Add the ruls to the rules buf
                            rule_bufs[table].append(tmp_rule_pair_str + '\n')
                            rule_cnts[table] += 1
                        else:
                            return 0
                    else:
                        return 0


        else:
            num_addr = 0
            #either src or dst add is not present
            if num_src_addr != 0:
                num_addr = num_src_addr
                str_m_itr = src_m_itr
                split_addr = split_src_addr
            elif num_dst_addr != 0:
                num_addr = num_dst_addr
                str_m_itr = dst_m_itr
                split_addr = split_dst_addr
               
            #Iterate over the addresses 
            for addr_itr in range (0,num_addr):
                if rule_strs != None:
                    #Get the original LOG rule
                    rule_str = rule_strs[rstr_index]
                    tmp_rule_str = rule_str
                    #replace the IP address str(which is with commas) with the IP address
                    tmp_rule_str = tmp_rule_str.replace(split_rule_str[str_m_itr+1],split_addr[addr_itr])
                    #print('New Rule 3 --> %s\n'%tmp_rule_str)
                    #Add the rule to the buffer
                    rule_bufs[table].append(tmp_rule_str + '\n')
                    rule_cnts[table] += 1
                    if rule_pair_strs != None:
                        #Get the sibling rule with DROP option
                        rule_pair_str = rule_pair_strs[rstr_index]
                        tmp_rule_pair_str = rule_pair_str
                        #replace the IP address str(Which is with commas) with the IP address
                        tmp_rule_pair_str = tmp_rule_pair_str.replace(split_rule_str[str_m_itr+1],split_addr[addr_itr])
                        #print('New Rule 4 --> %s\n'%tmp_rule_pair_str)
                        #Add the rule to the buffer
                        rule_bufs[table].append(tmp_rule_pair_str + '\n')
                        rule_cnts[table] += 1
                    else:
                        return 0
                else:
                    return 0
               

        total_num_add_comb = num_src_addr + num_dst_addr

        #print ('Total number of Address combos : %d\n'%(total_num_add_comb))
        return 1
    else:
        #print ('NO SIBLING\n')
        return 0

	
def iptables_rollback_common(fw_policies, type, orig_rules_filename):
	return iptables_restore_all_tables(orig_rules_filename, type, '-c', 0)

def iptables_rollback(fw_policies, orig_rules_filename):
	return iptables_rollback_common(fw_policies,
			aclRuleType.iptables, orig_rules_filename)

def ip6tables_rollback(fw_policies, orig_rules_filename):
	return iptables_rollback_common(fw_policies,
			aclRuleType.ip6tables, orig_rules_filename)


def iptables_install_common(fw_policies, type, new_filename):
	return iptables_restore_all_tables(new_filename, type, '-u', pvt_dot1x_mode)

def iptables_install(fw_policies, orig_restore_file, new_restore_file):
	return iptables_install_common(fw_policies,
			aclRuleType.iptables, new_restore_file)

def ip6tables_install(fw_policies, orig_restore_file, new_restore_file):
	return iptables_install_common(fw_policies,
			aclRuleType.ip6tables, new_restore_file)

def iptables_prepare_install_common(fw_policies, type,
		orig_rules_filename, new_rules_filename):
	rule_cnts = {}
	rule_newfilenames = {}
	rule_oldfilenames = {}
	rule_bufs = {}
	ret = 0

	# Initialize
	for table in iptables.kernel_tables:
		rule_cnts[table] = 0
		rule_newfilenames[table] = new_rules_filename + '.' + table
		rule_oldfilenames[table] = orig_rules_filename + '.' + table
		rule_bufs[table] = []

	# Create restore files for each table
	for fw_policy in fw_policies:
		rules = fw_policy.get_rules()
		if rules == None:
			continue

		rindex = 0
		while rindex < len(rules):
			r = rules[rindex]
			table = r.get_table()
			if table == None:
				table = 'filter'

			r_pair = None
			if r.get_type() == type:
				if r.has_sibling() != 0:
					r_pair = rules[rindex + 1]

				if rule_cnts[table] == 0:
					rule_bufs[table].append('\n#policy: %s\n' %fw_policy.get_name())

				rule_bufs[table].append('#\n#' +
						'line: %d' %r.get_lineno() +
						', rule: \'%s\'\n'
						%r.get_rule_str())

				if r_pair !=None:
					rule_bufs[table].append('#' +
					'(rule_pair) line: %d'
						%r_pair.get_lineno() +
						', rule: \'%s\'\n'
						%r_pair.get_rule_str())

				rule_strs = r.get_processed_rule_str_list()
				if r_pair != None:
					rule_pair_strs = r_pair.get_processed_rule_str_list()
				else:
					rule_pair_strs = None

				for rstr_index in range(0, len(rule_strs)):
                                        ret = iptables_log_rule_adjust(r, rule_strs, rstr_index, table, rule_bufs, rule_cnts, rule_pair_strs)
                                        if (ret != 1):
					    rule_str = rule_strs[rstr_index]
					    rule_bufs[table].append(rule_str + '\n')
					    rule_cnts[table] += 1

					    if rule_pair_strs != None:
						    rule_pair_str = rule_pair_strs[rstr_index]
						    rule_bufs[table].append(rule_pair_str + '\n')
						    rule_cnts[table] += 1

				if r_pair != None:
					rindex += 1

			rindex += 1


	for table in iptables.kernel_tables:
		if rule_cnts[table] > 0:
			if type == aclRuleType.iptables:
				cmd, ret, cmdout = iptables.save(table,
						rule_oldfilenames[table], '-c',
						verbose)
			else:
				cmd, ret, cmdout = ip6tables.save(table,
						rule_oldfilenames[table], '-c',
						verbose)
			if ret != 0:
				break

			try:
				f = open(rule_newfilenames[table], 'w')
			except IOError, e:
				log_error('Could not open file %s '
					%rule_newfilenames[table] +
					'(%s)' + str(e))
				ret = -1
				break

			f.write('*%s\n' %table)
			f.writelines(rule_bufs[table])
			f.write('COMMIT\n')
			f.close()
			log_debug('Created ' + rule_newfilenames[table])

			if debug == True:
				cat_file(rule_newfilenames[table])
		else:
			# No rules, we assume user wanted to not install
			# any rules here, flush rule
			if pvt_dot1x_mode == True:
			    log_verbose('not flushing iptables rules')
			    continue
			if type == aclRuleType.iptables:
				(cmd, fret, cmd_out) = iptables.flush(table, '',
								verbose)
			else:
				(cmd, fret, cmd_out) = ip6tables.flush(table,
								'', verbose)

	if ret != 0:
		# Delete all files
		for table in iptables.kernel_tables:
			remove_file(rule_oldfilenames[table])
			remove_file(rule_newfilenames[table])
		return -1

	return 0

def iptables_prepare_install(fw_policies, orig_rules_file,
		new_rules_file):
	return iptables_prepare_install_common(fw_policies,
		aclRuleType.iptables, orig_rules_file,
		new_rules_file)

	
def ip6tables_prepare_install(fw_policies, orig_rules_file,
		new_rules_file):
	return iptables_prepare_install_common(fw_policies,
		aclRuleType.ip6tables, orig_rules_file,
		new_rules_file)

def ebtables_rollback(fw_policies, orig_rules_filename):
	return ebtables_restore_all_tables(orig_rules_filename)

def delete_dot1x_ebtables_rules(cmd_prefix):
	err = 0
	dir_len = len(policy_dir)
	for rule_file in policy_files:
		file = policy_dir_default + rule_file[dir_len:]
		if os.path.exists(file):
			log_verbose (file +" exists")
		else:
			log_verbose (file +" doesn't exist")
			continue

		f = open(file,"r")
		lines =	f.readlines()
		for l in lines:
			sep = ' '
			w = l.split(sep)
			try:
				pos = w.index('-A')
			except ValueError:
				print("List does not contain -A")
				continue
			w[pos] = '-D'
			ch = w[pos+1]
			chain_list = ch.split(',')	
			for chain in chain_list:
				w[pos+1] = chain
				del_rule = sep.join(w)
				cmd = cmd_prefix + del_rule
				(cmdret, cmdout) = exec_command(cmd,verbose)
				if cmdret != 0:
					log_cmderr(cmd, cmdout)
					err += 1
					break
	return err

def ebtables_install(fw_policies, orig_rules_filename,
		new_rules_filename):
	rule_cnts = {}
	rule_newfilenames = {}
	rule_oldfilenames = {}
	rule_bufs = {}
	ret = 0
	err = 0


	# Initialize
	for table in ebtables.kernel_tables:
		rule_cnts[table] = 0
		rule_newfilenames[table] = new_rules_filename + '.' + table
		rule_oldfilenames[table] = orig_rules_filename + '.' + table
		rule_bufs[table] = []

	# Create restore files for each table, store contents in rule_bufs
	for fw_policy in fw_policies:
		rules = fw_policy.get_rules()
		if rules == None:
			continue

		for r in rules:
			table = r.get_table()
			if table == None:
				table = 'filter'

			if r.get_type() == aclRuleType.ebtables:
				rule_strs = r.get_processed_rule_str_list()
				for rule_str in rule_strs:
					rule_bufs[table].append(rule_str + '\n')
					rule_cnts[table] += 1

	err = 0
	for table in ebtables.kernel_tables:
		if rule_cnts[table] > 0:
			log_verbose('Installing ebtable rules for table %s'
					%table)
			cmd_prefix = ('/sbin/ebtables --atomic-file %s '
						%rule_newfilenames[table])

			#init the install file
			log_verbose('Initializing install file..')
			if pvt_dot1x_mode == False:
				cmd = cmd_prefix + '--atomic-init'
				(cmdret, cmdout) = exec_command(cmd, verbose)
				if cmdret != 0:
					log_cmderr(cmd, cmdout)
					err += 1
					break
			else:
				cmd = cmd_prefix + '--atomic-save'
				(cmdret, cmdout) = exec_command(cmd, verbose)
				if cmdret != 0:
					log_cmderr(cmd, cmdout)
					err += 1
					break
				log_verbose ("table is " + table)
				ret = delete_dot1x_ebtables_rules(cmd_prefix)
				if ret != 0:
					err += 1
					return ret
			for rbuf in rule_bufs[table]:
				if re.search('^#', rbuf) == None:
					cmd = cmd_prefix + rbuf
					(cmdret, cmdout) = exec_command(cmd,
								verbose)
					if cmdret != 0:
						log_cmderr(cmd, cmdout)
						err += 1
						remove_file(rule_newfilenames[table])
						break
			if err > 0:
				break

			log_verbose('Created ' + rule_newfilenames[table])
			if debug == 1:
				cat_file(rule_newfilenames[table])
			cmd, cmdret, cmdout = ebtables.restore(table,
						rule_newfilenames[table], '',
						verbose)
			if cmdret != 0:
				log_cmderr(cmd, cmdout)
				err += 1
				break
		else:
			if pvt_dot1x_mode == True:
			    log_verbose('Not flushing ebtables rules')
			    continue
			# If no rules for the table, just delete the saved file
			remove_file(rule_oldfilenames[table])

			# Also flush rules:
			# XXX: This is because today, we want to support
			# 'no rules in file' = 'no rules in kernel'
			(cmd, fret, cmd_out) = ebtables.flush(table, '',
							verbose)


	if err > 0:
		for t in ebtables.kernel_tables:
			# Lets restore tables we had flushed
			if os.path.exists(rule_oldfilenames[table]):
				cmd, cmdret, cmdout = ebtables.restore(table,
						rule_oldfilenames[table], '',
						verbose)
				if ret != 0:
					log_cmderr(cmd, cmdout)

			remove_file(rule_oldfilenames[table])
			remove_file(rule_newfilenames[table])
			if t == table:
				break
		return -1

	return 0

def ebtables_prepare_install(fw_policies, orig_rules_filename,
		new_rules_filename):
	return ebtables_save_all_tables(orig_rules_filename)

def iptables_flush_prepare(basefilename):
	return iptables_save_all_tables(basefilename, aclRuleType.iptables, 0)

def ip6tables_flush_prepare(basefilename):
	return iptables_save_all_tables(basefilename, aclRuleType.ip6tables, 0)

def ebtables_flush_prepare(basefilename):
	return ebtables_save_all_tables(basefilename)

def iptables_flush():
	err = 0
	for table in iptables.kernel_tables:
		log_verbose('Calling iptables flush for table ' + table)
		(cmd, ret, cmd_out) = iptables.flush(table, '', verbose)
		if ret != 0:
			log_cmderr(cmd, cmd_out)
			err += 1

	if err > 0:
		return -1

	return 0

def ip6tables_flush():
	err = 0
	for table in ip6tables.kernel_tables:
		log_verbose('Calling ip6tables flush for table ' + table)
		(cmd, ret, cmd_out) = ip6tables.flush(table, '', verbose)
		if ret != 0:
			log_cmderr(cmd, cmd_out)
			err += 1

	if err > 0:
		return -1

	return 0

def ebtables_flush():
	err = 0
	for table in ebtables.kernel_tables:
		log_verbose('Calling ebtables flush for table ' + table)
		(cmd, ret, cmd_out) = ebtables.flush(table, '', verbose)
		if ret != 0:
			log_cmderr(cmd, cmd_out)
			err += 1

	if err > 0:
		return -1

	return 0

def iptables_set_counters(counter_val):
	err = 0
	for table in iptables.kernel_tables:
		if counter_val == 0:
			log_verbose('Calling iptables zero counters for table '
					+ table)
			(cmd, ret, cmd_out) = iptables.zero_counters(table,
							'', verbose)
			if ret != 0:
				log_cmderr(cmd, cmd_out)
				err += 1

	if err > 0:
		return -1

	return 0


def ip6tables_set_counters(counter_val):
	err = 0
	for table in ip6tables.kernel_tables:
		if counter_val == 0:
			log_verbose('Calling ip6tables zero counters for table '
					+ table)
			(cmd, ret, cmd_out) = ip6tables.zero_counters(table,
							'', verbose)
			if ret != 0:
				log_cmderr(cmd, cmd_out)
				err += 1
	if err > 0:
		return -1

	return 0

def ebtables_set_counters(counter_val):
	err = 0
	for table in ebtables.kernel_tables:
		if counter_val == 0:
			log_verbose('Calling ebtables zero counters for table '
					+ table)
			(cmd, ret, cmd_out) = ebtables.zero_counters(table,
							'', verbose)
			if ret != 0:
				log_cmderr(cmd, cmd_out)
				err += 1
	if err > 0:
		return -1

	return 0

def iptables_list():
	err = 0
	for table in iptables.kernel_tables:
		log('TABLE %s :' %table)
		(cmd, ret, cmd_out) = iptables.list(table,
					('-v%s' % (' -n' * numeric)) + ((exact and ' -x') or ''), verbose)
		if ret != 0:
			log_cmderr(cmd, cmd_out)
			err += 1
		else:
			print '\n\n'
	if err > 0:
		return -1

	return 0

def ip6tables_list():
	err = 0
	for table in ip6tables.kernel_tables:
		log('TABLE %s :' %table)
		(cmd, ret, cmd_out) = ip6tables.list(table,
					('-v%s' % (' -n' * numeric)) + ((exact and ' -x') or ''), verbose)
		if ret != 0:
			log_cmderr(cmd, cmd_out)
			err += 1
		else:
			print '\n\n'
	if err > 0:
		return -1

	return 0

def ebtables_list():
	err = 0
	for table in ebtables.kernel_tables:
		log('TABLE %s :' %table)
		(cmd, ret, cmd_out) = ebtables.list(table,
					'--Lc', verbose)
		if ret != 0:
			log_cmderr(cmd, cmd_out)
			err += 1
		else:
			print '\n\n'
	if err > 0:
		return -1

	return 0


acl_handlers = {
	aclRuleType.iptables :
		{ 'prepare' : iptables_prepare_install,
		  'install' : iptables_install,
		  'rollback' : iptables_rollback,
		  'orig_rules_file' : 'iptables.save',
		  'new_rules_file' : 'iptables.restore',
		  'flush' : iptables_flush,
		  'flush_prepare' : iptables_flush_prepare,
		  'flush_rollback' : iptables_rollback,
		  'setcounter' : iptables_set_counters,
		  'list' : iptables_list},
	aclRuleType.ip6tables :
		{ 'prepare' : ip6tables_prepare_install,
		  'install' : ip6tables_install,
		  'rollback' : ip6tables_rollback,
		  'orig_rules_file' : 'ip6tables.save',
		  'new_rules_file' : 'ip6tables.restore',
		  'flush' : ip6tables_flush,
		  'flush_prepare' : ip6tables_flush_prepare,
		  'flush_rollback' : ip6tables_rollback,
		  'setcounter' : ip6tables_set_counters,
		  'list' : ip6tables_list},
	aclRuleType.ebtables :
		{ 'prepare' : ebtables_prepare_install,
		  'install' : ebtables_install,
		  'rollback' : ebtables_rollback,
		  'orig_rules_file' : 'ebtables.save',
		  'new_rules_file' : 'ebtables.restore',
		  'flush' : ebtables_flush,
		  'flush_prepare' : ebtables_flush_prepare,
		  'flush_rollback' : ebtables_rollback,
		  'setcounter' : ebtables_set_counters,
		  'list' : ebtables_list},
	}

def run_handler(fw_policies, handler_name):
	# Prepare rules for install
	for type, handlers in sorted(acl_handlers.iteritems()):
		hndlr = handlers.get(handler_name)
		if hndlr != None:
			log_debug('Running handler ' + handler_name +
				+  ' through rules of type %s'
				%aclRuleType.to_str(type))
			ret = hndlr(fw_policies)
			if ret != 0 :
				return -1

	return 0



def install_policies(fw_policies):
	ret = 0

	# prepare for install
	log_verbose('Running install prepare handlers ..')
	for type, handlers in sorted(acl_handlers.iteritems()):
		hndlr = handlers.get('prepare')
		if hndlr != None:
			log_debug('Running handler prepare ' +
				'through rules of type %s'
				%aclRuleType.to_str(type))
			ret = hndlr(fw_policies,
				scratchdir + handlers.get('orig_rules_file'),
				scratchdir + handlers.get('new_rules_file'))
			if ret != 0:
				return ret


	if hw_sync == True:
		log_verbose('Sending hw sync start signal ...');
		ret = hw_sync_start()
		if ret != 0:
			log('hw sync start notify returned err, aborting')
			return -1

	# install rules
	rollback_all = 0
	sync_cancel = 0
	log_verbose('Running install handlers ..')
	for type, handlers in sorted(acl_handlers.iteritems()):
		hndlr = handlers.get('install')
		if hndlr != None:
			log_verbose('Running handler install ' +
				'through rules of type %s'
				%aclRuleType.to_str(type))
			rules_file = scratchdir + handlers.get('new_rules_file')
			ret = hndlr(fw_policies, 
				scratchdir + handlers.get('orig_rules_file'),
				scratchdir + handlers.get('new_rules_file'))
			if ret != 0:
				if os.path.exists(rules_file):
					cat_file(rules_file)
				sync_cancel = 1
				break

	if ret == 0 and hw_sync == True:
		log_verbose('Sending hw sync signal ...');
		ret = hw_sync_commit()
		if ret != 0:
			rollback_all = 1

	if ret != 0:
		if sync_cancel == 1 and hw_sync == True:
			log_verbose('Sending hw sync cancel signal ..')
			hw_sync_cancel()

		log('Rolling back ..')
		failed_type = type
		for type, handlers in sorted(acl_handlers.iteritems()):

			#roll back for all types < failed_type
			# XX: Normally type >= failed_type is the right thing
			# to do, except in the ebtables case, where we
			# also do a flush. It does not harm so, also include
			# restore for the type that failed
			if rollback_all == 0 and type > failed_type:
				break

			hndlr = handlers.get('rollback')
			if hndlr != None:
				log_debug('Running handler rollback' +
					' through rules of type %s'
					%aclRuleType.to_str(type))
				rollret = hndlr(fw_policies, scratchdir +
					handlers.get('orig_rules_file'))
				if rollret != 0:
					log_warn('roll back handler for rule' +
						' type ' + aclRuleType.to_str(
							type) + ' failed')

	if ret == 0 and hw_sync == True:
		log_verbose('Sending hw sync end signal ...');
		retend = hw_sync_end()
		if retend != 0:
			log_warn('hw sync end notify returned err')

	return ret

def flush_rules(rule_type_str):
	ret = 0

	rule_type = aclRuleType.from_str_short(rule_type_str)
	if rule_type == -1 and rule_type_str != "all":
		log_error('Invalid flush arg')
		return -1

	# prepare 
	log_verbose('Running prepare handlers ..')
	for type, handlers in sorted(acl_handlers.iteritems()):
		if rule_type == type or rule_type_str == 'all':
			hndlr = handlers.get('flush_prepare')
			if hndlr != None:
				log_verbose('Running handler prepare ' +
					'through rules of type %s'
					%aclRuleType.to_str(type))
				ret = hndlr(scratchdir +
					handlers.get('orig_rules_file'))
				if ret != 0:
					return ret


	if hw_sync == True:
		log_verbose('Sending hw sync start signal ...');
		ret = hw_sync_start()
		if ret != 0:
			log('hw sync start notify returned err, aborting')
			return -1

	# flushing  rules
	rollback_all = 0
	sync_cancel = 0
	log_verbose('Running flush handlers ..')
	for type, handlers in sorted(acl_handlers.iteritems()):
		if rule_type == type or rule_type_str == 'all':
			hndlr = handlers.get('flush')
			if hndlr != None:
				log_verbose('Running handler flush ' +
					'through rules of type %s'
					%aclRuleType.to_str(type))
				ret = hndlr()
				if ret != 0:
					sync_cancel = 1
					break

	if ret == 0 and hw_sync == True:
		log_verbose('Sending hw sync signal ...');
		ret = hw_sync_commit()
		if ret != 0:
			rollback_all = 1

	if ret != 0:
		if sync_cancel == 1 and hw_sync == True:
			log_verbose('Sending hw sync cancel signal ..')
			hw_sync_cancel()

		log('Rolling back ..')
		failed_type = type
		for type, handlers in sorted(acl_handlers.iteritems()):
			if rule_type == type or rule_type_str == 'all':

				#roll back for all types < failed_type
				# XX: Normally type >= failed_type is the
				# right thing to do, except in the ebtables
				# case, where we also do a flush. It does not
				# harm so, also include restore for the type
				# that failed
				if rollback_all == 0 and type > failed_type:
					break

				hndlr = handlers.get('rollback')
				if hndlr != None:
					log_debug('Running handler rollback' +
						' through rules of type %s'
						%aclRuleType.to_str(type))
					rollret = hndlr(None, scratchdir +
						handlers.get('orig_rules_file'))
					if rollret != 0:
						log_warn('roll back handler '
							+ 'for rule type ' +
							aclRuleType.to_str(
							type) + ' failed')

	if ret == 0 and hw_sync == True:
		log_verbose('Sending hw sync end signal ...');
		retend = hw_sync_end()
		if retend != 0:
			log_warn('hw sync end notify returned err')

	return ret


def set_counters(rule_type_str, counter_val):
	err = 0

	rule_type = aclRuleType.from_str_short(rule_type_str)
	if rule_type == -1 and rule_type_str != "all":
		log_error('Invalid setcounters arg')
		return -1

	for type, handlers in sorted(acl_handlers.iteritems()):
		if rule_type == type or rule_type_str == 'all':
			setcounterfunc = handlers.get('setcounter')
			if setcounterfunc != None:
				log_verbose('Setting counters to zero for ' +
					'rules of type %s'
					%aclRuleType.to_str(type))
				ret = setcounterfunc(counter_val)
				if ret != 0:
					err += 1
	if err > 0:
		return -1

	return 0

def list_rules(rule_type_str):
	err = 0

	rule_type = aclRuleType.from_str_short(rule_type_str)
	if rule_type == -1 and rule_type_str != "all":
		log_error('Invalid list rules arg')
		return -1

	for type, handlers in sorted(acl_handlers.iteritems()):
		if rule_type == type or rule_type_str == 'all':
			listfunc = handlers.get('list')
			if listfunc != None:
				msg = ('Listing rules of type %s:'
						%aclRuleType.to_str(type))
				print '-' * len(msg)
				log(msg)
				print '-' * len(msg)
				ret = listfunc()
				if ret != 0:
					err += 1
	if err > 0:
		return -1

	return 0

def read_policy_file(fw_policy_file):
	"""Calls acl module to read
	   and parse rules in the rule file """
	global env_vars

	log('Reading rule file %s ...' %fw_policy_file)
	fw_policy = aclPolicy(fw_policy_file, env_vars)
	if not fw_policy:
		return None
	dup_check_reqd = not pvt_dot1x_mode
	log('Processing rules in file %s ...' %fw_policy_file)
	ret = fw_policy.process(dup_check_reqd)
	if ret:
		log_verbose('Could not process rules in policy %s'
				%fw_policy.get_name())
		del(fw_policy)
		return None
	return fw_policy

def get_policy_files_from_dir(policy_dir):
	""" Calls read_acl_policy_file for all files
	    under dir policy_dir """
	global policy_files
	err = 0

	try:
		r_dir = os.path.abspath(policy_dir)
		if not r_dir.endswith("/"):
			r_dir += "/"
			policy_file_list = os.listdir(r_dir)
	except Exception, e:
		log_error('failed to read rules directory %s (%s)'
					%(policy_dir, str(e)))
		return None

	for policy_file in sorted(policy_file_list):
		# skip hidden files
		if re.search('^\.|.*~$', os.path.basename(policy_file)):
			continue
		policy_files.append('%s/%s' %(policy_dir,policy_file))

def get_policy_files_from_conf(policy_conffile):
	global env_vars
	global policy_files

	try:
		f = open(policy_conffile, 'r')
		lines = f.readlines()
		f.close()
	except IOError, e:
		log_warn('failed to open policy config file (%s)' %(str(e)))
		pass
		return

	lineno = 1
	for l in lines:
		l = l.strip(' \t\n\r')
		l = l.strip(' ')
		if not l:
			lineno += 1
			continue
		# if comment
		if l[0] == '#':
			lineno += 1
			continue
		# if env variable
		m = re.match(r"(\w+)[\s]*=[\s]*(.*$)", l)
		if m:
			mlist = m.groups()
			if mlist and len(mlist) == 2 and all(mlist):
				env_vars[mlist[0]]=mlist[1]
		elif l[:8] == 'include ':
			# if include
			try :
				pfiles = sorted(glob.glob(l.split()[1]))
				if cmdline_args.lastgood:
					# replace path with lastgood files dir
					lastgoodpfiles = ['%s/%s' %(varrundir_lastgoodpolicyfiles,
										os.path.basename(p)) for p in pfiles]
					policy_files.extend(lastgoodpfiles)
				else:
					policy_files.extend(pfiles)
			except Exception, e:
				log_parseerr(policy_conffile, l, lineno, errstr=str(e))
				pass
		else:
			log_parseerr(policy_conffile, l, lineno)
		lineno += 1

def read_policy_files():
	global policy_files
	err = 0
	fw_policies = []

	for policy_file in policy_files:
		# skip hidden files
		if re.search('^\.|.*~$', os.path.basename(policy_file)):
			continue
		fw_policy = read_policy_file(policy_file)
		if fw_policy:
			fw_policies.append(fw_policy)
		else:
			if not cmdline_args.dry_run:
				return None
			# else, if we are only checking the file, 
			# lets continue
			err += 1
	if err > 0:
		return None
	return fw_policies

def log_init():
	global log_handler

	if log_method == "syslog":
		syslog.openlog("cl-acltool", syslog.LOG_CONS | syslog.LOG_PID,
				syslog.LOG_DAEMON)
		log_handler = log_handler_syslog
	else:
		log_handler = log_handler_stdout

def init():
	""" initialization """
	ret = createdir(scratchdir)
	if ret == -1:
		log_error('Could not create scratchdir ' + scratchdir)
		return -1

	ret = createdir(cachedir)
	if ret == -1:
		log_error('Could not create ' + cachedir)
		return -1

	ret = createdir(varrundir_lastgoodpolicyfiles)
	if ret == -1:
		log_error('Could not create ' + varrundir_lastgoodpolicyfiles)
		return -1

	if hw_sync == True:
		log_verbose('Initializing hw communication ..')
		ret = hw_init()
		if ret != 0:
			return -1

	return 0


def deinit():
	""" Cleanup """
	removedir(scratchdir)

def save_installed_state(cmdline_args):

	touch_file(donefile)
	if cmdline_args.lastgood:
		return
	files = glob.glob((varrundir_lastgoodpolicyfiles + '/*'))
	for f in files:
		try:
			os.remove(f)
		except OSError, e:
			log_error('Could not remove file %s: %s \n'%(f, str(e)))
	devnull = open(os.devnull, 'w')
	if not cmdline_args.policy_file and not cmdline_args.policy_dir:
		# copy policy.conf, If the user used it
		subprocess.call(['/bin/cp', '-f', policy_conf,
				varrundir_lastgoodpolicyfiles], stderr=devnull)

	map((lambda p: subprocess.call(['/bin/cp', '-f', p,
	    varrundir_lastgoodpolicyfiles], stderr=devnull)), policy_files)
	devnull.close()
	return

def isVX():
    try:
        # Check whether we are running in VX.
        if "cumulus,vx" in subprocess.check_output('/usr/bin/platform-detect'):
            return True
    except (subprocess.CalledProcessError, OSError):
            pass

if __name__ == "__main__":
	""" main function """
	descr = 'acl policy and rule administration'
	fw_policies = []
	ret = 0

	if not os.geteuid() == 0:
		print 'Error: Must be root to run this command'
		exit(1)

	default_vx = isVX()

	arg_parser = argparse.ArgumentParser(description=descr)

	# Command line arg parser
	#
	group = arg_parser.add_mutually_exclusive_group(required=True)
	group.add_argument('-i', '--install', dest='install',
			   action='store_true',
			   help='Install acl rules')
	group.add_argument('--hw-sync', dest='hw_sync',
			action='store_true', help=argparse.SUPPRESS)
	group.add_argument('-F', '--flush', dest='flush_type',
			choices=['all', 'ip', 'ip6',
			'eb'],
			help='flush rules')
	group.add_argument('-Z', '--zero-counters', dest='zero_counters',
			choices=['all', 'ip', 'ip6',
			'eb'], help='Zero counters')
	group.add_argument('-L', '--list', dest='list',
			choices=['all', 'ip', 'ip6',
			'eb'], help='List rules')
	group.add_argument('-V', '--version', dest='version',
			action='store_true', help='show version')

	# XXX: Support append (?) maybe. keep it hidden,
	# ie iptables-restore --noflush
	#group.add_argument('--append', dest='append', action='store_true',
	#		    help=argparse.SUPPRESS)

	arg_parser.add_argument('-p', '--policy', dest='policy_file',
				action='append',
				help='acl policy file name')

	arg_parser.add_argument('-P', '--policy-dir', dest='policy_dir',
				help='policy dir.')
	arg_parser.add_argument('-p8021x', '--private_dot1x_mode', dest='pvt_dot1x_mode',
				action='store_true', help=argparse.SUPPRESS)
	arg_parser.add_argument('-n', '--dry-run', dest='dry_run',
				action='store_true', help='dry run')
	arg_parser.add_argument('-N', '--numeric', dest='numeric',
				action='store_true', help='do not resolve hostnames')
	arg_parser.add_argument('-v', '--verbose', dest='verbose',
				action='store_true', help='verbose')
	arg_parser.add_argument('-x', '--exact', dest='exact',
				action='store_true', help='expand numbers (display exact values)')
	arg_parser.add_argument('-d', '--debug', dest='debug',
				action='store_true', help=argparse.SUPPRESS)
	arg_parser.add_argument('-q', '--quiet', dest='quiet',
				action='store_true', help=argparse.SUPPRESS)
	arg_parser.add_argument('-w', '--hw-wait', dest='hw_wait', type=int,
				help=argparse.SUPPRESS)
	arg_parser.add_argument('--no-hw-sync', dest='no_hw_sync',
				action='store_true', default=default_vx, help=argparse.SUPPRESS)
	arg_parser.add_argument('--last-good', dest='lastgood',
				action='store_true', help='install last good acl policies')
	arg_parser.add_argument('--log', dest='log',
				choices=['syslog'],
				help=argparse.SUPPRESS)

	argcomplete.autocomplete(arg_parser)

	# Parse command line arguments
	cmdline_args = arg_parser.parse_args()

	if cmdline_args.log != None:
		log_method = cmdline_args.log

	log_init()

	if cmdline_args.version == True:
		log('version: %s' %version)
		exit(0)

	if cmdline_args.policy_file and not cmdline_args.install:
		log_error('policy_file needed only with -i\n')
		arg_parser.print_help()
		exit(1)

	if cmdline_args.policy_dir and not cmdline_args.install:
		log_error('policy_dir needed only with -i\n')
		arg_parser.print_help()
		exit(1)

	if cmdline_args.dry_run and not cmdline_args.install:
		log_error('dry run only supported with -i\n')
		arg_parser.print_help()
		exit(1)

	if cmdline_args.lastgood and not cmdline_args.install:
		log_error('option last good is only supported with -i\n')
		arg_parser.print_help()
		exit(1)

	if cmdline_args.exact:
		exact = True

	if cmdline_args.verbose:
		verbose = True

	if cmdline_args.numeric:
		numeric = True

	if cmdline_args.debug:
		debug = 1
		verbose = True

	if cmdline_args.quiet:
		quiet = True
		debug = True
		verbose = True

	if cmdline_args.no_hw_sync:
		if default_vx:
		   log_warn('Detected platform is Cumulus VX')

                log_warn('Running in no-hw-sync mode. No ' +
				'rules will be programmed in hw')
		hw_sync = False

	if cmdline_args.hw_wait:
		hw_wait = cmdline_args.hw_wait

	if cmdline_args.hw_sync:
		# Send sync signal to hw
		ret = init()
		if ret != 0:
			log('init failed, aborting')
			exit(1)

		log_msg_begin('Sending hw sync signal')
		ret = hw_sync_commit()
		log_msg_end(ret)
		deinit()
		exit(ret)
	elif cmdline_args.zero_counters:
		log_msg_begin('Setting counters to zero')
		ret = set_counters(cmdline_args.zero_counters, 0)
		log_msg_end(ret)
		exit(ret)
	elif cmdline_args.list:
		try:
			ret = list_rules(cmdline_args.list)
		except IOError, e:
			if e.errno == errno.EPIPE:
				pass
			else:
				raise
		except:
			raise
		exit(ret)

	if (cmdline_args.policy_dir):
		policy_dir = cmdline_args.policy_dir
		if cmdline_args.pvt_dot1x_mode:
		    pvt_dot1x_mode = True
	elif (cmdline_args.lastgood):
		policy_dir = varrundir_lastgoodpolicyfiles
	else:
		policy_dir = None

	if policy_dir and not os.path.exists(policy_dir):
		log_error('Cannot find policy dir ' +
			policy_dir)
		exit(1)

	# Initialize
	ret = init()
	if ret != 0:
		log('init failed, aborting')
		deinit()
		exit(1)

	if cmdline_args.flush_type != None:
		log_msg_begin('Flushing rules')
		ret = flush_rules(cmdline_args.flush_type)
		log_msg_end(ret)
		deinit()
		exit(ret)

	if cmdline_args.policy_file:
		filesnotfound = [f for f in cmdline_args.policy_file
							if not os.path.exists(f)]
		if filesnotfound:
			log_error('Cannot find policy files: %s' %str(filesnotfound))
			deinit()
			exit(1)
		policy_files = cmdline_args.policy_file
		if cmdline_args.pvt_dot1x_mode:
		    pvt_dot1x_mode = True
	elif cmdline_args.lastgood:
		lastgood_conf = '%s/%s' %(varrundir_lastgoodpolicyfiles,
										policy_conf_name)
		if os.path.exists(lastgood_conf):
			# If lastgood conf exists, use that to read files and
			# variables. But note that the paths read from the conf
			# file has to now be relative to the lastgood dir
			get_policy_files_from_conf(lastgood_conf)
		else:
			# else read the files in sorted order
			get_policy_files_from_dir(policy_dir)
	elif policy_dir:
		get_policy_files_from_dir(policy_dir)
	else:
		# Get policy_files from conf file
		get_policy_files_from_conf(policy_conf)

	if not policy_files:
		log_error('no policy files found')
		deinit()
		exit(1)

	log_verbose('Installing policy files : %s' %(', '.join(policy_files)))
	fw_policies = read_policy_files()
	if not fw_policies:
		ret = 1

	if cmdline_args.dry_run:
		# We are all done, exit
		log_msg_end(ret)
		deinit()
		exit(ret)
	
	if ret:
		log('No acl policies to install, ... aborting')
		deinit()
		exit (1)

	num_rules = 0
	for f in fw_policies:
		num_rules = num_rules + len(f.get_rules())
	
	if num_rules == 0:
		log('No valid rules found, ... aborting')
		deinit()
		exit (1)

	log_verbose('Found %d acl policies' %len(fw_policies))

	if debug == True:
		log('Dumping processed acl policy ...')
		for p in fw_policies:
			p.dump()

	if cmdline_args.install == True:
		log_msg_begin('Installing acl policy')
		ret = install_policies(fw_policies)
		log_msg_end(ret)
		if ret == 0:
			save_installed_state(cmdline_args)

	deinit()

	exit(ret)
