
/* Authors: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
 *
 * Copyright (C) 2003,2004,2005 Tresys Technology, LLC
 *	This program is free software; you can redistribute it and/or modify
 *  	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, version 2.
 */

/* 
 * dismod.c
 *
 * Test program to the contents of a binary policy in text
 * form.
 *
 * 	dismod binary_mod_file
 */

#include <getopt.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#include <sepol/policydb/policydb.h>
#include <sepol/policydb/services.h>
#include <sepol/policydb/conditional.h>
#include <sepol/policydb/link.h>
#include <sepol/policydb/module.h>
#include <sepol/policydb/util.h>
#include <sepol/policydb/polcaps.h>

#include <byteswap.h>
#include <endian.h>

#if __BYTE_ORDER == __LITTLE_ENDIAN
#define le32_to_cpu(x) (x)
#else
#define le32_to_cpu(x) bswap_32(x)
#endif

#define DISPLAY_AVBLOCK_COND_AVTAB	0
#define DISPLAY_AVBLOCK_UNCOND_AVTAB	1
#define DISPLAY_AVBLOCK_ROLE_TYPE_NODE	2 /* unused? */
#define DISPLAY_AVBLOCK_ROLE_TRANS	3
#define DISPLAY_AVBLOCK_ROLE_ALLOW	4
#define DISPLAY_AVBLOCK_REQUIRES	5
#define DISPLAY_AVBLOCK_DECLARES	6
#define DISPLAY_AVBLOCK_FILENAME_TRANS	7

static policydb_t policydb;
extern unsigned int ss_initialized;

int policyvers = MOD_POLICYDB_VERSION_BASE;

static const char *symbol_labels[9] = {
	"commons",
	"classes", "roles  ", "types  ", "users  ", "bools  ",
	"levels ", "cats   ", "attribs"
};

static __attribute__((__noreturn__)) void usage(const char *progname)
{
	printf("usage:  %s binary_pol_file\n\n", progname);
	exit(1);
}

static void render_access_mask(uint32_t mask, uint32_t class, policydb_t * p,
			       FILE * fp)
{
	char *perm;
	fprintf(fp, "{");
	perm = sepol_av_to_string(p, class, mask);
	if (perm)
		fprintf(fp, "%s ", perm);
	fprintf(fp, "}");
}

static void render_access_bitmap(ebitmap_t * map, uint32_t class,
				 policydb_t * p, FILE * fp)
{
	unsigned int i;
	char *perm;
	fprintf(fp, "{");
	for (i = ebitmap_startbit(map); i < ebitmap_length(map); i++) {
		if (ebitmap_get_bit(map, i)) {
			perm = sepol_av_to_string(p, class, UINT32_C(1) << i);
			if (perm)
				fprintf(fp, " %s", perm);
		}
	}
	fprintf(fp, " }");
}

static void display_id(policydb_t * p, FILE * fp, uint32_t symbol_type,
		       uint32_t symbol_value, const char *prefix)
{
	char *id = p->sym_val_to_name[symbol_type][symbol_value];
	scope_datum_t *scope =
	    (scope_datum_t *) hashtab_search(p->scope[symbol_type].table, id);
	assert(scope != NULL);
	if (scope->scope == SCOPE_REQ) {
		fprintf(fp, " [%s%s]", prefix, id);
	} else {
		fprintf(fp, " %s%s", prefix, id);
	}
}

static int display_type_set(type_set_t * set, uint32_t flags, policydb_t * policy,
		     FILE * fp)
{
	unsigned int i, num_types;

	if (set->flags & TYPE_STAR) {
		fprintf(fp, " * ");
		return 0;
	} else if (set->flags & TYPE_COMP) {
		fprintf(fp, " ~");
	}

	num_types = 0;
	if (flags & RULE_SELF) {
		num_types++;
	}

	for (i = ebitmap_startbit(&set->types); i < ebitmap_length(&set->types);
	     i++) {
		if (!ebitmap_get_bit(&set->types, i))
			continue;
		num_types++;
		if (num_types > 1)
			break;
	}

	if (num_types <= 1) {
		for (i = ebitmap_startbit(&set->negset);
		     i < ebitmap_length(&set->negset); i++) {
			if (!ebitmap_get_bit(&set->negset, i))
				continue;
			num_types++;
			if (num_types > 1)
				break;
		}
	}

	if (num_types > 1)
		fprintf(fp, "{");

	for (i = ebitmap_startbit(&set->types); i < ebitmap_length(&set->types);
	     i++) {
		if (!ebitmap_get_bit(&set->types, i))
			continue;
		display_id(policy, fp, SYM_TYPES, i, "");
	}

	for (i = ebitmap_startbit(&set->negset);
	     i < ebitmap_length(&set->negset); i++) {
		if (!ebitmap_get_bit(&set->negset, i))
			continue;
		display_id(policy, fp, SYM_TYPES, i, "-");
	}

	if (flags & RULE_SELF) {
		fprintf(fp, " self");
	}

	if (num_types > 1)
		fprintf(fp, " }");

	return 0;
}

static int display_mod_role_set(role_set_t * roles, policydb_t * p, FILE * fp)
{
	unsigned int i, num = 0;

	if (roles->flags & ROLE_STAR) {
		fprintf(fp, " * ");
		return 0;
	} else if (roles->flags & ROLE_COMP) {
		fprintf(fp, " ~");
	}

	for (i = ebitmap_startbit(&roles->roles);
	     i < ebitmap_length(&roles->roles); i++) {
		if (!ebitmap_get_bit(&roles->roles, i))
			continue;
		num++;
		if (num > 1) {
			fprintf(fp, "{");
			break;
		}
	}

	for (i = ebitmap_startbit(&roles->roles);
	     i < ebitmap_length(&roles->roles); i++) {
		if (ebitmap_get_bit(&roles->roles, i))
			display_id(p, fp, SYM_ROLES, i, "");
	}

	if (num > 1)
		fprintf(fp, " }");

	return 0;

}

static int display_avrule(avrule_t * avrule, policydb_t * policy,
		   FILE * fp)
{
	class_perm_node_t *cur;
	int num_classes;

	if (avrule == NULL) {
		fprintf(fp, "  <empty>\n");
		return 0;
	}
	if (avrule->specified & AVRULE_AV) {
		if (avrule->specified & AVRULE_ALLOWED) {
			fprintf(fp, "  allow");
		}
		if (avrule->specified & AVRULE_AUDITALLOW) {
			fprintf(fp, "  auditallow ");
		}
		if (avrule->specified & AVRULE_DONTAUDIT) {
			fprintf(fp, "  dontaudit");
		}
	} else if (avrule->specified & AVRULE_TYPE) {
		if (avrule->specified & AVRULE_TRANSITION) {
			fprintf(fp, "  type_transition");
		}
		if (avrule->specified & AVRULE_MEMBER) {
			fprintf(fp, "  type_member");
		}
		if (avrule->specified & AVRULE_CHANGE) {
			fprintf(fp, "  type_change");
		}
	} else if (avrule->specified & AVRULE_NEVERALLOW) {
		fprintf(fp, "  neverallow");
	} else if (avrule->specified & AVRULE_XPERMS) {
		if (avrule->specified & AVRULE_XPERMS_ALLOWED)
			fprintf(fp, "allowxperm ");
		else if (avrule->specified & AVRULE_XPERMS_AUDITALLOW)
			fprintf(fp, "auditallowxperm ");
		else if (avrule->specified & AVRULE_XPERMS_DONTAUDIT)
			fprintf(fp, "dontauditxperm ");
	} else {
		fprintf(fp, "     ERROR: no valid rule type specified\n");
		return -1;
	}

	if (display_type_set(&avrule->stypes, 0, policy, fp))
		return -1;

	if (display_type_set(&avrule->ttypes, avrule->flags, policy, fp))
		return -1;

	fprintf(fp, " :");
	cur = avrule->perms;
	num_classes = 0;
	while (cur) {
		num_classes++;
		if (num_classes > 1)
			break;
		cur = cur->next;
	}

	if (num_classes > 1)
		fprintf(fp, " {");

	cur = avrule->perms;
	while (cur) {
		display_id(policy, fp, SYM_CLASSES, cur->tclass - 1, "");
		cur = cur->next;
	}

	if (num_classes > 1)
		fprintf(fp, " }");
	fprintf(fp, " ");

	if (avrule->specified & (AVRULE_AV | AVRULE_NEVERALLOW)) {
		render_access_mask(avrule->perms->data, avrule->perms->tclass,
				   policy, fp);
	} else if (avrule->specified & AVRULE_TYPE) {
		display_id(policy, fp, SYM_TYPES, avrule->perms->data - 1, "");
	} else if (avrule->specified & AVRULE_XPERMS) {
		avtab_extended_perms_t xperms;
		int i;

		if (avrule->xperms->specified == AVRULE_XPERMS_IOCTLFUNCTION)
			xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION;
		else if (avrule->xperms->specified == AVRULE_XPERMS_IOCTLDRIVER)
			xperms.specified = AVTAB_XPERMS_IOCTLDRIVER;
		else {
			fprintf(fp, "     ERROR: no valid xperms specified\n");
			return -1;
		}

		xperms.driver = avrule->xperms->driver;
		for (i = 0; i < EXTENDED_PERMS_LEN; i++)
			xperms.perms[i] = avrule->xperms->perms[i];

		fprintf(fp, "%s", sepol_extended_perms_to_string(&xperms));
	}

	fprintf(fp, ";\n");

	return 0;
}

static int display_type_callback(hashtab_key_t key, hashtab_datum_t datum, void *data)
{
	type_datum_t *type;
	FILE *fp;
	unsigned int i, first_attrib = 1;

	type = (type_datum_t *) datum;
	fp = (FILE *) data;

	if (type->primary) {
		display_id(&policydb, fp, SYM_TYPES, type->s.value - 1, "");
		fprintf(fp, " [%d]: ", type->s.value);
	} else {
		/* as that aliases have no value of their own and that
		 * they can never be required by a module, use this
		 * alternative way of displaying a name */
		fprintf(fp, " %s [%d]: ", (char *)key, type->s.value);
	}
	if (type->flavor == TYPE_ATTRIB) {
		fprintf(fp, "attribute for types");
		for (i = ebitmap_startbit(&type->types);
		     i < ebitmap_length(&type->types); i++) {
			if (!ebitmap_get_bit(&type->types, i))
				continue;
			if (first_attrib) {
				first_attrib = 0;
			} else {
				fprintf(fp, ",");
			}
			display_id(&policydb, fp, SYM_TYPES, i, "");
		}
	} else if (type->primary) {
		fprintf(fp, "type");
	} else {
		fprintf(fp, "alias for type");
		display_id(&policydb, fp, SYM_TYPES, type->s.value - 1, "");
	}
	fprintf(fp, " flags:%x\n", type->flags);

	return 0;
}

static int display_types(policydb_t * p, FILE * fp)
{
	if (hashtab_map(p->p_types.table, display_type_callback, fp))
		return -1;
	return 0;
}

static int display_users(policydb_t * p, FILE * fp)
{
	unsigned int i, j;
	ebitmap_t *bitmap;
	for (i = 0; i < p->p_users.nprim; i++) {
		display_id(p, fp, SYM_USERS, i, "");
		fprintf(fp, ":");
		bitmap = &(p->user_val_to_struct[i]->roles.roles);
		for (j = ebitmap_startbit(bitmap); j < ebitmap_length(bitmap);
		     j++) {
			if (ebitmap_get_bit(bitmap, j)) {
				display_id(p, fp, SYM_ROLES, j, "");
			}
		}
		fprintf(fp, "\n");
	}
	return 0;
}

static int display_bools(policydb_t * p, FILE * fp)
{
	unsigned int i;

	for (i = 0; i < p->p_bools.nprim; i++) {
		display_id(p, fp, SYM_BOOLS, i, "");
		fprintf(fp, " : %d\n", p->bool_val_to_struct[i]->state);
	}
	return 0;
}

static void display_expr(policydb_t * p, cond_expr_t * exp, FILE * fp)
{

	cond_expr_t *cur;
	for (cur = exp; cur != NULL; cur = cur->next) {
		switch (cur->expr_type) {
		case COND_BOOL:
			fprintf(fp, "%s ",
				p->p_bool_val_to_name[cur->bool - 1]);
			break;
		case COND_NOT:
			fprintf(fp, "! ");
			break;
		case COND_OR:
			fprintf(fp, "|| ");
			break;
		case COND_AND:
			fprintf(fp, "&& ");
			break;
		case COND_XOR:
			fprintf(fp, "^ ");
			break;
		case COND_EQ:
			fprintf(fp, "== ");
			break;
		case COND_NEQ:
			fprintf(fp, "!= ");
			break;
		default:
			fprintf(fp, "error!");
			break;
		}
	}
}

static void display_policycon(FILE * fp)
{
	/* There was an attempt to implement this at one time.  Look through
	 * git history to find it. */
	fprintf(fp, "Sorry, not implemented\n");
}

static void display_initial_sids(policydb_t * p, FILE * fp)
{
	ocontext_t *cur;
	char *user, *role, *type;

	fprintf(fp, "Initial SIDs:\n");
	for (cur = p->ocontexts[OCON_ISID]; cur != NULL; cur = cur->next) {
		user = p->p_user_val_to_name[cur->context[0].user - 1];
		role = p->p_role_val_to_name[cur->context[0].role - 1];
		type = p->p_type_val_to_name[cur->context[0].type - 1];
		fprintf(fp, "\tsid %d, context %s:%s:%s\n",
			cur->sid[0], user, role, type);
	}
#if 0
	fprintf(fp, "Policy Initial SIDs:\n");
	for (cur = p->ocontexts[OCON_POLICYISID]; cur != NULL; cur = cur->next) {
		user = p->p_user_val_to_name[cur->context[0].user - 1];
		role = p->p_role_val_to_name[cur->context[0].role - 1];
		type = p->p_type_val_to_name[cur->context[0].type - 1];
		fprintf(fp, "\t%s: sid %d, context %s:%s:%s\n",
			cur->u.name, cur->sid[0], user, role, type);
	}
#endif
}

static void display_class_set(ebitmap_t *classes, policydb_t *p, FILE *fp)
{
	unsigned int i, num = 0;

	for (i = ebitmap_startbit(classes); i < ebitmap_length(classes); i++) {
		if (!ebitmap_get_bit(classes, i))
			continue;
		num++;
		if (num > 1) {
			fprintf(fp, "{");
			break;
		}
	}

	for (i = ebitmap_startbit(classes); i < ebitmap_length(classes); i++) {
		if (ebitmap_get_bit(classes, i))
			display_id(p, fp, SYM_CLASSES, i, "");
	}

	if (num > 1)
		fprintf(fp, " }");
}

static void display_role_trans(role_trans_rule_t * tr, policydb_t * p, FILE * fp)
{
	for (; tr; tr = tr->next) {
		fprintf(fp, "role transition ");
		display_mod_role_set(&tr->roles, p, fp);
		display_type_set(&tr->types, 0, p, fp);
		fprintf(fp, " :");
		display_class_set(&tr->classes, p, fp);
		display_id(p, fp, SYM_ROLES, tr->new_role - 1, "");
		fprintf(fp, "\n");
	}
}

static void display_role_allow(role_allow_rule_t * ra, policydb_t * p, FILE * fp)
{
	for (; ra; ra = ra->next) {
		fprintf(fp, "role allow ");
		display_mod_role_set(&ra->roles, p, fp);
		display_mod_role_set(&ra->new_roles, p, fp);
		fprintf(fp, "\n");
	}
}

static void display_filename_trans(filename_trans_rule_t * tr, policydb_t * p, FILE * fp)
{
	fprintf(fp, "filename transition");
	for (; tr; tr = tr->next) {
		display_type_set(&tr->stypes, 0, p, fp);
		display_type_set(&tr->ttypes, 0, p, fp);
		display_id(p, fp, SYM_CLASSES, tr->tclass - 1, ":");
		display_id(p, fp, SYM_TYPES, tr->otype - 1, "");
		fprintf(fp, " %s\n", tr->name);
	}
}

static int role_display_callback(hashtab_key_t key __attribute__((unused)),
			  hashtab_datum_t datum, void *data)
{
	role_datum_t *role;
	FILE *fp;

	role = (role_datum_t *) datum;
	fp = (FILE *) data;

	fprintf(fp, "role:");
	display_id(&policydb, fp, SYM_ROLES, role->s.value - 1, "");
	fprintf(fp, " types: ");
	display_type_set(&role->types, 0, &policydb, fp);
	fprintf(fp, "\n");

	return 0;
}

static int display_scope_index(scope_index_t * indices, policydb_t * p,
			       FILE * out_fp)
{
	unsigned int i;
	for (i = 0; i < SYM_NUM; i++) {
		unsigned int any_found = 0, j;
		fprintf(out_fp, "%s:", symbol_labels[i]);
		for (j = ebitmap_startbit(&indices->scope[i]);
		     j < ebitmap_length(&indices->scope[i]); j++) {
			if (ebitmap_get_bit(&indices->scope[i], j)) {
				any_found = 1;
				fprintf(out_fp, " %s",
					p->sym_val_to_name[i][j]);
				if (i == SYM_CLASSES) {
					if (j < indices->class_perms_len) {
						render_access_bitmap(indices->
								     class_perms_map
								     + j, j + 1,
								     p, out_fp);
					} else {
						fprintf(out_fp,
							"<no perms known>");
					}
				}
			}
		}
		if (!any_found) {
			fprintf(out_fp, " <empty>");
		}
		fprintf(out_fp, "\n");
	}
	return 0;
}

#if 0
int display_cond_expressions(policydb_t * p, FILE * fp)
{
	cond_node_t *cur;
	cond_av_list_t *av_cur;
	for (cur = p->cond_list; cur != NULL; cur = cur->next) {
		fprintf(fp, "expression: ");
		display_expr(p, cur->expr, fp);
		fprintf(fp, "current state: %d\n", cur->cur_state);
		fprintf(fp, "True list:\n");
		for (av_cur = cur->true_list; av_cur != NULL;
		     av_cur = av_cur->next) {
			fprintf(fp, "\t");
			render_av_rule(&av_cur->node->key, &av_cur->node->datum,
				       RENDER_CONDITIONAL, p, fp);
		}
		fprintf(fp, "False list:\n");
		for (av_cur = cur->false_list; av_cur != NULL;
		     av_cur = av_cur->next) {
			fprintf(fp, "\t");
			render_av_rule(&av_cur->node->key, &av_cur->node->datum,
				       RENDER_CONDITIONAL, p, fp);
		}
	}
	return 0;
}

int change_bool(char *name, int state, policydb_t * p, FILE * fp)
{
	cond_bool_datum_t *bool;

	bool = hashtab_search(p->p_bools.table, name);
	if (bool == NULL) {
		fprintf(fp, "Could not find bool %s\n", name);
		return -1;
	}
	bool->state = state;
	evaluate_conds(p);
	return 0;
}
#endif

static int display_avdecl(avrule_decl_t * decl, int field,
		   policydb_t * policy, FILE * out_fp)
{
	fprintf(out_fp, "decl %u:%s\n", decl->decl_id,
		(decl->enabled ? " [enabled]" : ""));
	switch (field) {
	case DISPLAY_AVBLOCK_COND_AVTAB:{
			cond_list_t *cond = decl->cond_list;
			avrule_t *avrule;
			while (cond) {
				fprintf(out_fp, "expression: ");
				display_expr(&policydb, cond->expr, out_fp);
				fprintf(out_fp, "current state: %d\n",
					cond->cur_state);
				fprintf(out_fp, "True list:\n");
				avrule = cond->avtrue_list;
				while (avrule) {
					display_avrule(avrule,
						       &policydb, out_fp);
					avrule = avrule->next;
				}
				fprintf(out_fp, "False list:\n");
				avrule = cond->avfalse_list;
				while (avrule) {
					display_avrule(avrule,
						       &policydb, out_fp);
					avrule = avrule->next;
				}
				cond = cond->next;
			}
			break;
		}
	case DISPLAY_AVBLOCK_UNCOND_AVTAB:{
			avrule_t *avrule = decl->avrules;
			if (avrule == NULL) {
				fprintf(out_fp, "  <empty>\n");
			}
			while (avrule != NULL) {
				if (display_avrule(avrule, policy, out_fp))
					return -1;
				avrule = avrule->next;
			}
			break;
		}
	case DISPLAY_AVBLOCK_ROLE_TYPE_NODE:{	/* role_type_node */
			break;
		}
	case DISPLAY_AVBLOCK_ROLE_TRANS:{
			display_role_trans(decl->role_tr_rules, policy, out_fp);
			break;
		}
	case DISPLAY_AVBLOCK_ROLE_ALLOW:{
			display_role_allow(decl->role_allow_rules, policy,
					   out_fp);
			break;
		}
	case DISPLAY_AVBLOCK_REQUIRES:{
			if (display_scope_index
			    (&decl->required, policy, out_fp)) {
				return -1;
			}
			break;
		}
	case DISPLAY_AVBLOCK_DECLARES:{
			if (display_scope_index
			    (&decl->declared, policy, out_fp)) {
				return -1;
			}
			break;
		}
	case DISPLAY_AVBLOCK_FILENAME_TRANS:
		display_filename_trans(decl->filename_trans_rules, policy,
				       out_fp);
		break;
	default:{
			assert(0);
		}
	}
	return 0;		/* should never get here */
}

static int display_avblock(int field, policydb_t * policy,
		    FILE * out_fp)
{
	avrule_block_t *block = policydb.global;
	while (block != NULL) {
		avrule_decl_t *decl = block->branch_list;
		fprintf(out_fp, "--- begin avrule block ---\n");
		while (decl != NULL) {
			if (display_avdecl(decl, field, policy, out_fp)) {
				return -1;
			}
			decl = decl->next;
		}
		block = block->next;
	}
	return 0;
}

static int display_handle_unknown(policydb_t * p, FILE * out_fp)
{
	if (p->handle_unknown == ALLOW_UNKNOWN)
		fprintf(out_fp, "Allow unknown classes and perms\n");
	else if (p->handle_unknown == DENY_UNKNOWN)
		fprintf(out_fp, "Deny unknown classes and perms\n");
	else if (p->handle_unknown == REJECT_UNKNOWN)
		fprintf(out_fp, "Reject unknown classes and perms\n");
	return 0;
}

static int read_policy(char *filename, policydb_t * policy)
{
	FILE *in_fp;
	struct policy_file f;
	int retval;
	uint32_t buf[1];

	if ((in_fp = fopen(filename, "rb")) == NULL) {
		fprintf(stderr, "Can't open '%s':  %s\n",
			filename, strerror(errno));
		exit(1);
	}
	policy_file_init(&f);
	f.type = PF_USE_STDIO;
	f.fp = in_fp;

	/* peek at the first byte.  if they are indicative of a
	   package use the package reader, otherwise use the normal
	   policy reader */
	if (fread(buf, sizeof(uint32_t), 1, in_fp) != 1) {
		fprintf(stderr, "Could not read from policy.\n");
		exit(1);
	}
	rewind(in_fp);
	if (le32_to_cpu(buf[0]) == SEPOL_MODULE_PACKAGE_MAGIC) {
		sepol_module_package_t *package;
		if (sepol_module_package_create(&package)) {
			fprintf(stderr, "%s:  Out of memory!\n", __FUNCTION__);
			exit(1);
		}
		sepol_policydb_free(package->policy);
		package->policy = (sepol_policydb_t *) policy;
		package->file_contexts = NULL;
		retval =
		    sepol_module_package_read(package,
					      (sepol_policy_file_t *) & f, 1);
		package->policy = NULL;
		sepol_module_package_free(package);
	} else {
		if (policydb_init(policy)) {
			fprintf(stderr, "%s:  Out of memory!\n", __FUNCTION__);
			exit(1);
		}
		retval = policydb_read(policy, &f, 1);
	}
	fclose(in_fp);
	return retval;
}

static void link_module(policydb_t * base, FILE * out_fp)
{
	char module_name[80] = { 0 };
	int ret;
	policydb_t module, *mods = &module;

	if (base->policy_type != POLICY_BASE) {
		printf("Can only link if initial file was a base policy.\n");
		return;
	}
	printf("\nModule filename: ");
	if (fgets(module_name, sizeof(module_name), stdin) == NULL) {
		fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__,
				strerror(errno));
		exit(1);
	}

	module_name[strlen(module_name) - 1] = '\0';	/* remove LF */
	if (module_name[0] == '\0') {
		return;
	}

	/* read the binary policy */
	fprintf(out_fp, "Reading module...\n");
	if (read_policy(module_name, mods)) {
		fprintf(stderr,
			"%s:  error(s) encountered while loading policy\n",
			module_name);
		exit(1);
	}
	if (module.policy_type != POLICY_MOD) {
		fprintf(stderr, "This file is not a loadable policy module.\n");
		exit(1);
	}
	if (policydb_index_classes(&module) ||
	    policydb_index_others(NULL, &module, 0)) {
		fprintf(stderr, "Could not index module.\n");
		exit(1);
	}
	ret = link_modules(NULL, base, &mods, 1, 0);
	if (ret != 0) {
		printf("Link failed (error %d)\n", ret);
		printf("(You will probably need to restart dismod.)\n");
	}
	policydb_destroy(&module);
	return;
}

static void display_policycaps(policydb_t * p, FILE * fp)
{
	ebitmap_node_t *node;
	const char *capname;
	char buf[64];
	unsigned int i;

	fprintf(fp, "policy capabilities:\n");
	ebitmap_for_each_positive_bit(&p->policycaps, node, i) {
		capname = sepol_polcap_getname(i);
		if (capname == NULL) {
			snprintf(buf, sizeof(buf), "unknown (%u)", i);
			capname = buf;
		}
		fprintf(fp, "\t%s\n", capname);
	}
}

static int menu(void)
{
	printf("\nSelect a command:\n");
	printf("1)  display unconditional AVTAB\n");
	printf("2)  display conditional AVTAB\n");
	printf("3)  display users\n");
	printf("4)  display bools\n");
	printf("5)  display roles\n");
	printf("6)  display types, attributes, and aliases\n");
	printf("7)  display role transitions\n");
	printf("8)  display role allows\n");
	printf("9)  Display policycon\n");
	printf("0)  Display initial SIDs\n");
	printf("\n");
	printf("a)  Display avrule requirements\n");
	printf("b)  Display avrule declarations\n");
	printf("c)  Display policy capabilities\n");
	printf("l)  Link in a module\n");
	printf("u)  Display the unknown handling setting\n");
	printf("F)  Display filename_trans rules\n");
	printf("\n");
	printf("f)  set output file\n");
	printf("m)  display menu\n");
	printf("q)  quit\n");
	return 0;
}

int main(int argc, char **argv)
{
	FILE *out_fp = stdout;
	char ans[81], OutfileName[121];

	if (argc != 2)
		usage(argv[0]);

	/* read the binary policy */
	fprintf(out_fp, "Reading policy...\n");
	if (policydb_init(&policydb)) {
		fprintf(stderr, "%s:  Out of memory!\n", __FUNCTION__);
		exit(1);
	}
	if (read_policy(argv[1], &policydb)) {
		fprintf(stderr,
			"%s:  error(s) encountered while loading policy\n",
			argv[0]);
		exit(1);
	}

	if (policydb.policy_type != POLICY_BASE &&
	    policydb.policy_type != POLICY_MOD) {
		fprintf(stderr,
			"This file is neither a base nor loadable policy module.\n");
		exit(1);
	}

	if (policydb_index_classes(&policydb)) {
		fprintf(stderr, "Error indexing classes\n");
		exit(1);
	}

	if (policydb_index_others(NULL, &policydb, 1)) {
		fprintf(stderr, "Error indexing others\n");
		exit(1);
	}

	if (policydb.policy_type == POLICY_BASE) {
		printf("Binary base policy file loaded.\n");
	} else {
		printf("Binary policy module file loaded.\n");
		printf("Module name: %s\n", policydb.name);
		printf("Module version: %s\n", policydb.version);
	}

	printf("Policy version: %d\n\n", policydb.policyvers);
	menu();
	for (;;) {
		printf("\nCommand (\'m\' for menu):  ");
		if (fgets(ans, sizeof(ans), stdin) == NULL) {
			fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__,
					strerror(errno));
			continue;
		}

		switch (ans[0]) {

		case '1':
			fprintf(out_fp, "unconditional avtab:\n");
			display_avblock(DISPLAY_AVBLOCK_UNCOND_AVTAB,
					&policydb, out_fp);
			break;
		case '2':
			fprintf(out_fp, "conditional avtab:\n");
			display_avblock(DISPLAY_AVBLOCK_COND_AVTAB,
					&policydb, out_fp);
			break;
		case '3':
			display_users(&policydb, out_fp);
			break;
		case '4':
			display_bools(&policydb, out_fp);
			break;
		case '5':
			if (hashtab_map
			    (policydb.p_roles.table, role_display_callback,
			     out_fp))
				exit(1);
			break;
		case '6':
			if (display_types(&policydb, out_fp)) {
				fprintf(stderr, "Error displaying types\n");
				exit(1);
			}
			break;
		case '7':
			fprintf(out_fp, "role transitions:\n");
			display_avblock(DISPLAY_AVBLOCK_ROLE_TRANS,
					&policydb, out_fp);
			break;
		case '8':
			fprintf(out_fp, "role allows:\n");
			display_avblock(DISPLAY_AVBLOCK_ROLE_ALLOW,
					&policydb, out_fp);
			break;
		case '9':
			display_policycon(out_fp);
			break;
		case '0':
			display_initial_sids(&policydb, out_fp);
			break;
		case 'a':
			fprintf(out_fp, "avrule block requirements:\n");
			display_avblock(DISPLAY_AVBLOCK_REQUIRES,
					&policydb, out_fp);
			break;
		case 'b':
			fprintf(out_fp, "avrule block declarations:\n");
			display_avblock(DISPLAY_AVBLOCK_DECLARES,
					&policydb, out_fp);
			break;
		case 'c':
			display_policycaps(&policydb, out_fp);
			break;
		case 'u':
		case 'U':
			display_handle_unknown(&policydb, out_fp);
			break;
		case 'f':
			printf
			    ("\nFilename for output (<CR> for screen output): ");
			if (fgets(OutfileName, sizeof(OutfileName), stdin) == NULL) {
				fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__,
						strerror(errno));
				break;
			}
			OutfileName[strlen(OutfileName) - 1] = '\0';	/* fix_string (remove LF) */
			if (strlen(OutfileName) == 0)
				out_fp = stdout;
			else if ((out_fp = fopen(OutfileName, "w")) == NULL) {
				fprintf(stderr, "Cannot open output file %s\n",
					OutfileName);
				out_fp = stdout;
			}
			if (out_fp != stdout)
				printf("\nOutput to file: %s\n", OutfileName);
			break;
		case 'F':
			fprintf(out_fp, "filename_trans rules:\n");
			display_avblock(DISPLAY_AVBLOCK_FILENAME_TRANS,
					&policydb, out_fp);
			break;
		case 'l':
			link_module(&policydb, out_fp);
			break;
		case 'q':
			policydb_destroy(&policydb);
			exit(0);
			break;
		case 'm':
			menu();
			break;
		default:
			printf("\nInvalid choice\n");
			menu();
			break;

		}
	}
	exit(EXIT_SUCCESS);
}
