libsemanage: add support for HLL to CIL compilers

An HLL to CIL compiler must exist in the compiler_directory path which
is configubrable in semanage.conf. By default, this path is
/usr/libexec/selinux/hll/. The compiler name needs to match the HLL
language extension. For example, for pp files,
/usr/libexec/selinux/hll/pp must exist.

The HLL infrastructure uncompresses the HLL module and pipes the data to
the appropriate CIL compiler. The output CIL from the compiler is read
from another pipe, compressed, and saved to the module store as a cached
CIL file. This file will be used on all subsequent policy builds, unless
a new module is installed with the same name at the same priority, at
which point the cache is deleted and is subsequently rebuilt and cached.

A new option is added to semanage.conf, ignore_cache, which if set to
true will cause the cached CIL files to be ignored and all HLL files to
be recompiled and the resulting CIL to be recached.

Signed-off-by: Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com>
diff --git a/libsemanage/include/semanage/handle.h b/libsemanage/include/semanage/handle.h
index 00a396f..f85b5fe 100644
--- a/libsemanage/include/semanage/handle.h
+++ b/libsemanage/include/semanage/handle.h
@@ -66,6 +66,11 @@
  * 1 for yes, 0 for no (default) */
 void semanage_set_rebuild(semanage_handle_t * handle, int do_rebuild);
 
+/* Fills *compiler_path with the location of the hll compiler sh->conf->compiler_directory_path
+ * corresponding to lang_ext.
+ * Upon success returns 0, -1 on error. */
+int semanage_get_hll_compiler_path(semanage_handle_t *sh, char *lang_ext, char **compiler_path);
+
 /* create the store if it does not exist, this only has an effect on 
  * direct connections and must be called before semanage_connect 
  * 1 for yes, 0 for no (default) */
@@ -146,6 +151,12 @@
 /* Set whether or not to preserve the needless unused branch of tunables */
 void semanage_set_preserve_tunables(semanage_handle_t * handle, int preserve_tunables);
 
+/* Get the flag value for whether or not caching is ignored for compiled CIL modules from HLL files */
+int semanage_get_ignore_module_cache(semanage_handle_t *handle);
+
+/* Set semanage_handle flag for whether or not to ignore caching of compiled CIL modules from HLL files */
+void semanage_set_ignore_module_cache(semanage_handle_t *handle, int ignore_module_cache);
+
 /* META NOTES
  *
  * For all functions a non-negative number indicates success. For some
diff --git a/libsemanage/man/man5/semanage.conf.5 b/libsemanage/man/man5/semanage.conf.5
index a696302..c1db9df 100644
--- a/libsemanage/man/man5/semanage.conf.5
+++ b/libsemanage/man/man5/semanage.conf.5
@@ -32,6 +32,15 @@
 Specify an alternative root path to use for the store. The default is "/"
 
 .TP
+.B compiler-directory
+Specify an alternative directory that contains HLL to CIL compilers. The default value is "/usr/libexec/selinux/hll".
+
+.TP
+.B ignore-module-cache
+Whether or not to ignore the cache of CIL modules compiled from HLL. It can be set to either "true" or "false" and is set to "false" by default.
+If the cache is ignored, then all CIL modules are recompiled from their HLL modules.
+
+.TP
 .B policy-version 
 When generating the policy, by default
 .BR semanage
diff --git a/libsemanage/src/conf-parse.y b/libsemanage/src/conf-parse.y
index 56f26d2..9dfafd9 100644
--- a/libsemanage/src/conf-parse.y
+++ b/libsemanage/src/conf-parse.y
@@ -38,6 +38,7 @@
 extern char *semanage_text;
 
 static int parse_module_store(char *arg);
+static int parse_compiler_path(char *arg);
 static void semanage_conf_external_prog_destroy(external_prog_t *ep);
 static int new_external_prog(external_prog_t **chain);
 
@@ -56,7 +57,7 @@
         char *s;
 }
 
-%token MODULE_STORE VERSION EXPAND_CHECK FILE_MODE SAVE_PREVIOUS SAVE_LINKED TARGET_PLATFORM
+%token MODULE_STORE VERSION EXPAND_CHECK FILE_MODE SAVE_PREVIOUS SAVE_LINKED TARGET_PLATFORM COMPILER_DIR IGNORE_MODULE_CACHE
 %token LOAD_POLICY_START SETFILES_START SEFCONTEXT_COMPILE_START DISABLE_GENHOMEDIRCON HANDLE_UNKNOWN USEPASSWD IGNOREDIRS
 %token BZIP_BLOCKSIZE BZIP_SMALL
 %token VERIFY_MOD_START VERIFY_LINKED_START VERIFY_KERNEL_START BLOCK_END
@@ -78,6 +79,8 @@
 single_opt:     module_store
         |       version
         |       target_platform
+        |       compiler_dir
+        |       ignore_module_cache
         |       expand_check
         |       file_mode
         |       save_previous
@@ -99,6 +102,25 @@
 
         ;
 
+compiler_dir:       COMPILER_DIR '=' ARG  {
+                        if (parse_compiler_path($3) != 0) {
+                                parse_errors++;
+                                YYABORT;
+                        }
+                }
+        ;
+
+ignore_module_cache:	IGNORE_MODULE_CACHE '=' ARG  {
+							if (strcasecmp($3, "true") == 0)
+								current_conf->ignore_module_cache = 1;
+							else if (strcasecmp($3, "false") == 0)
+								current_conf->ignore_module_cache = 0;
+							else {
+								yyerror("disable-caching can only be 'true' or 'false'");
+							}
+						}
+        ;
+
 version:        VERSION '=' ARG  {
                         current_conf->policyvers = atoi($3);
                         free($3);
@@ -287,6 +309,7 @@
 	conf->store_type = SEMANAGE_CON_DIRECT;
 	conf->store_path = strdup(basename(selinux_policy_root()));
 	conf->ignoredirs = NULL;
+	conf->compiler_directory_path = strdup("/usr/libexec/selinux/hll");
 	conf->policyvers = sepol_policy_kern_vers_max();
 	conf->target_platform = SEPOL_TARGET_SELINUX;
 	conf->expand_check = 1;
@@ -295,6 +318,7 @@
 	conf->file_mode = 0644;
 	conf->bzip_blocksize = 9;
 	conf->bzip_small = 0;
+	conf->ignore_module_cache = 0;
 
 	conf->save_previous = 0;
 	conf->save_linked = 0;
@@ -395,6 +419,7 @@
 	if (conf != NULL) {
 		free(conf->store_path);
 		free(conf->ignoredirs);
+		free(conf->compiler_directory_path);
 		semanage_conf_external_prog_destroy(conf->load_policy);
 		semanage_conf_external_prog_destroy(conf->setfiles);
 		semanage_conf_external_prog_destroy(conf->sefcontext_compile);
@@ -459,6 +484,16 @@
 	return 0;
 }
 
+static int parse_compiler_path(char *arg)
+{
+	if (arg == NULL) {
+		return -1;
+	}
+	free(current_conf->compiler_directory_path);
+	current_conf->compiler_directory_path = strdup(arg);
+	return 0;
+}
+
 /* Helper function; called whenever configuration file specifies
  * another external program.  Returns 0 on success, -1 if out of
  * memory.
diff --git a/libsemanage/src/conf-scan.l b/libsemanage/src/conf-scan.l
index 080c198..39cdd8f 100644
--- a/libsemanage/src/conf-scan.l
+++ b/libsemanage/src/conf-scan.l
@@ -40,6 +40,8 @@
 
 #.*               /* ignore comments */
 module-store      return MODULE_STORE;
+compiler-directory	return COMPILER_DIR;
+ignore-module-cache return IGNORE_MODULE_CACHE;
 policy-version    return VERSION;
 target-platform   return TARGET_PLATFORM;
 expand-check      return EXPAND_CHECK;
diff --git a/libsemanage/src/direct_api.c b/libsemanage/src/direct_api.c
index 3c200d0..cdf48bf 100644
--- a/libsemanage/src/direct_api.c
+++ b/libsemanage/src/direct_api.c
@@ -54,6 +54,10 @@
 #include "database_policydb.h"
 #include "policy.h"
 #include <sys/mman.h>
+#include <sys/wait.h>
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
 
 static void semanage_direct_destroy(semanage_handle_t * sh);
 static int semanage_direct_disconnect(semanage_handle_t * sh);
@@ -593,6 +597,351 @@
 	return retval;
 }
 
+static int read_from_pipe_to_data(semanage_handle_t *sh, size_t initial_len, int fd, char **out_data_read, size_t *out_read_len)
+{
+	size_t max_len = initial_len;
+	size_t read_len = 0;
+	size_t data_read_len = 0;
+	char *data_read = NULL;
+
+	if (max_len <= 0) {
+		max_len = 1;
+	}
+	data_read = malloc(max_len * sizeof(*data_read));
+	if (data_read == NULL) {
+		ERR(sh, "Failed to malloc, out of memory.\n");
+		return -1;
+	}
+
+	while ((read_len = read(fd, data_read + data_read_len, max_len - data_read_len)) > 0) {
+		data_read_len += read_len;
+		if (data_read_len == max_len) {
+			max_len *= 2;
+			data_read = realloc(data_read, max_len);
+			if (data_read == NULL) {
+				ERR(sh, "Failed to realloc, out of memory.\n");
+				return -1;
+			}
+		}
+	}
+
+	*out_read_len = data_read_len;
+	*out_data_read = data_read;
+
+	return 0;
+}
+
+static int semanage_pipe_data(semanage_handle_t *sh, char *path, char *in_data, size_t in_data_len, char **out_data, size_t *out_data_len, char **err_data, size_t *err_data_len)
+{
+	int input_fd[2];
+	int output_fd[2];
+	int err_fd[2];
+	pid_t pid;
+	char *data_read = NULL;
+	char *err_data_read = NULL;
+	int retval;
+	int status = 0;
+	size_t initial_len;
+	size_t data_read_len = 0;
+	size_t err_data_read_len = 0;
+	struct sigaction old_signal;
+	struct sigaction new_signal;
+	new_signal.sa_handler = SIG_IGN;
+	sigemptyset(&new_signal.sa_mask);
+	new_signal.sa_flags = 0;
+	/* This is needed in case the read end of input_fd is closed causing a SIGPIPE signal to be sent.
+	 * If SIGPIPE is not caught, the signal will cause semanage to terminate immediately. The sigaction below
+	 * creates a new_signal that ignores SIGPIPE allowing the write to exit cleanly.
+	 *
+	 * Another sigaction is called in cleanup to restore the original behavior when a SIGPIPE is received.
+	 */
+	sigaction(SIGPIPE, &new_signal, &old_signal);
+
+	retval = pipe(input_fd);
+	if (retval == -1) {
+		ERR(sh, "Unable to create pipe for input pipe: %s\n", strerror(errno));
+		goto cleanup;
+	}
+	retval = pipe(output_fd);
+	if (retval == -1) {
+		ERR(sh, "Unable to create pipe for output pipe: %s\n", strerror(errno));
+		goto cleanup;
+	}
+	retval = pipe(err_fd);
+	if (retval == -1) {
+		ERR(sh, "Unable to create pipe for error pipe: %s\n", strerror(errno));
+		goto cleanup;
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		ERR(sh, "Unable to fork from parent: %s.", strerror(errno));
+		retval = -1;
+		goto cleanup;
+	} else if (pid == 0) {
+		retval = dup2(input_fd[PIPE_READ], STDIN_FILENO);
+		if (retval == -1) {
+			ERR(sh, "Unable to dup2 input pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+		retval = dup2(output_fd[PIPE_WRITE], STDOUT_FILENO);
+		if (retval == -1) {
+			ERR(sh, "Unable to dup2 output pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+		retval = dup2(err_fd[PIPE_WRITE], STDERR_FILENO);
+		if (retval == -1) {
+			ERR(sh, "Unable to dup2 error pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		retval = close(input_fd[PIPE_WRITE]);
+		if (retval == -1) {
+			ERR(sh, "Unable to close input pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+		retval = close(output_fd[PIPE_READ]);
+		if (retval == -1) {
+			ERR(sh, "Unable to close output pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+		retval = close(err_fd[PIPE_READ]);
+		if (retval == -1) {
+			ERR(sh, "Unable to close error pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+		retval = execl(path, path, NULL);
+		if (retval == -1) {
+			ERR(sh, "Unable to execute %s : %s\n", path, strerror(errno));
+			_exit(EXIT_FAILURE);
+		}
+	} else {
+		retval = close(input_fd[PIPE_READ]);
+		input_fd[PIPE_READ] = -1;
+		if (retval == -1) {
+			ERR(sh, "Unable to close read end of input pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		retval = close(output_fd[PIPE_WRITE]);
+		output_fd[PIPE_WRITE] = -1;
+		if (retval == -1) {
+			ERR(sh, "Unable to close write end of output pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		retval = close(err_fd[PIPE_WRITE]);
+		err_fd[PIPE_WRITE] = -1;
+		if (retval == -1) {
+			ERR(sh, "Unable to close write end of error pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		retval = write(input_fd[PIPE_WRITE], in_data, in_data_len);
+		if (retval == -1) {
+			ERR(sh, "Failed to write data to input pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+		retval = close(input_fd[PIPE_WRITE]);
+		input_fd[PIPE_WRITE] = -1;
+		if (retval == -1) {
+			ERR(sh, "Unable to close write end of input pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		initial_len = 1 << 17;
+		retval = read_from_pipe_to_data(sh, initial_len, output_fd[PIPE_READ], &data_read, &data_read_len);
+		if (retval != 0) {
+			goto cleanup;
+		}
+		retval = close(output_fd[PIPE_READ]);
+		output_fd[PIPE_READ] = -1;
+		if (retval == -1) {
+			ERR(sh, "Unable to close read end of output pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		initial_len = 1 << 9;
+		retval = read_from_pipe_to_data(sh, initial_len, err_fd[PIPE_READ], &err_data_read, &err_data_read_len);
+		if (retval != 0) {
+			goto cleanup;
+		}
+		retval = close(err_fd[PIPE_READ]);
+		err_fd[PIPE_READ] = -1;
+		if (retval == -1) {
+			ERR(sh, "Unable to close read end of error pipe: %s\n", strerror(errno));
+			goto cleanup;
+		}
+
+		if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status)) {
+			ERR(sh, "Child process %s did not exit cleanly.", path);
+			retval = -1;
+			goto cleanup;
+		}
+		if (WEXITSTATUS(status) != 0) {
+			ERR(sh, "Child process %s failed with code: %d.", path, WEXITSTATUS(status));
+			retval = -1;
+			goto cleanup;
+		}
+	}
+
+	retval = 0;
+
+cleanup:
+	sigaction(SIGPIPE, &old_signal, NULL);
+
+	if (data_read != NULL) {
+		*out_data = data_read;
+		*out_data_len = data_read_len;
+	}
+
+	if (err_data_read != NULL) {
+		*err_data = err_data_read;
+		*err_data_len = err_data_read_len;
+	}
+
+	if (output_fd[PIPE_READ] != -1) {
+		close(output_fd[PIPE_READ]);
+	}
+	if (output_fd[PIPE_WRITE] != -1) {
+		close(output_fd[PIPE_WRITE]);
+	}
+	if (err_fd[PIPE_READ] != -1) {
+		close(err_fd[PIPE_READ]);
+	}
+	if (err_fd[PIPE_WRITE] != -1) {
+		close(err_fd[PIPE_WRITE]);
+	}
+	if (input_fd[PIPE_READ] != -1) {
+		close(input_fd[PIPE_READ]);
+	}
+	if (input_fd[PIPE_WRITE] != -1) {
+		close(input_fd[PIPE_WRITE]);
+	}
+
+	return retval;
+}
+
+static int semanage_compile_hll(semanage_handle_t *sh,
+				semanage_module_info_t *modinfos,
+				int num_modinfos)
+{
+	char cil_path[PATH_MAX];
+	char hll_path[PATH_MAX];
+	char *compiler_path = NULL;
+	char *cil_data = NULL;
+	char *err_data = NULL;
+	char *hll_data = NULL;
+	char *start = NULL;
+	char *end = NULL;
+	ssize_t hll_data_len = 0;
+	ssize_t bzip_status;
+	int status = 0;
+	int i, compressed, in_fd;
+	size_t cil_data_len;
+	size_t err_data_len;
+
+	assert(sh);
+	assert(modinfos);
+
+	for (i = 0; i < num_modinfos; i++) {
+		if (!strcasecmp(modinfos[i].lang_ext, "cil")) {
+			continue;
+		}
+
+		status = semanage_module_get_path(
+				sh,
+				&modinfos[i],
+				SEMANAGE_MODULE_PATH_CIL,
+				cil_path,
+				sizeof(cil_path));
+		if (status != 0) {
+			goto cleanup;
+		}
+
+		if (semanage_get_ignore_module_cache(sh) == 0 &&
+			access(cil_path, F_OK) == 0) {
+			continue;
+		}
+
+		status = semanage_get_hll_compiler_path(sh, modinfos[i].lang_ext, &compiler_path);
+		if (status != 0) {
+			goto cleanup;
+		}
+
+		status = semanage_module_get_path(
+				sh,
+				&modinfos[i],
+				SEMANAGE_MODULE_PATH_HLL,
+				hll_path,
+				sizeof(hll_path));
+		if (status != 0) {
+			goto cleanup;
+		}
+
+		if ((in_fd = open(hll_path, O_RDONLY)) == -1) {
+			ERR(sh, "Unable to open %s\n", hll_path);
+			status = -1;
+			goto cleanup;
+		}
+
+		if ((hll_data_len = map_file(sh, in_fd, &hll_data, &compressed)) <= 0) {
+			ERR(sh, "Unable to read file %s\n", hll_path);
+			status = -1;
+			goto cleanup;
+		}
+
+		status = semanage_pipe_data(sh, compiler_path, hll_data, (size_t)hll_data_len, &cil_data, &cil_data_len, &err_data, &err_data_len);
+		if (err_data_len > 0) {
+			for (start = end = err_data; end < err_data + err_data_len; end++) {
+				if (*end == '\n') {
+					fprintf(stderr, "%s: ", modinfos[i].name);
+					fwrite(start, 1, end - start + 1, stderr);
+					start = end + 1;
+				}
+			}
+
+			if (end != start) {
+				fprintf(stderr, "%s: ", modinfos[i].name);
+				fwrite(start, 1, end - start, stderr);
+				fprintf(stderr, "\n");
+			}
+		}
+		if (status != 0) {
+			goto cleanup;
+		}
+
+		bzip_status = bzip(sh, cil_path, cil_data, cil_data_len);
+		if (bzip_status == -1) {
+			ERR(sh, "Failed to bzip %s\n", cil_path);
+			status = -1;
+			goto cleanup;
+		}
+
+		if (hll_data_len > 0) munmap(hll_data, hll_data_len);
+		hll_data_len = 0;
+
+		free(cil_data);
+		free(err_data);
+		free(compiler_path);
+		cil_data = NULL;
+		err_data = NULL;
+		compiler_path = NULL;
+		cil_data_len = 0;
+		err_data_len = 0;
+	}
+
+	status = 0;
+
+cleanup:
+	if (hll_data_len > 0) munmap(hll_data, hll_data_len);
+	free(cil_data);
+	free(err_data);
+	free(compiler_path);
+	return status;
+}
+
+
 /********************* direct API functions ********************/
 
 /* Commits all changes in sandbox to the actual kernel policy.
@@ -605,9 +954,10 @@
 	size_t fc_buffer_len = 0;
 	const char *ofilename = NULL;
 	const char *path;
-	int retval = -1, num_modfiles = 0, i;
+	int retval = -1, num_modinfos = 0, i;
 	sepol_policydb_t *out = NULL;
 	struct cil_db *cildb = NULL;
+	semanage_module_info_t *modinfos = NULL;
 
 	/* Declare some variables */
 	int modified = 0, fcontexts_modified, ports_modified,
@@ -710,16 +1060,26 @@
 	if (sh->do_rebuild || modified) {
 		/* =================== Module expansion =============== */
 
-		retval = semanage_get_modules_names(sh, &mod_filenames, &num_modfiles);
-		if (retval < 0)
-			goto cleanup;
-
-		if (num_modfiles == 0) {
-			ERR(sh, "No active modules.\n");
+		retval = semanage_get_active_modules(sh, &modinfos, &num_modinfos);
+		if (retval < 0) {
 			goto cleanup;
 		}
 
-		retval = semanage_verify_modules(sh, mod_filenames, num_modfiles);
+		if (num_modinfos == 0) {
+			goto cleanup;
+		}
+
+		retval = semanage_compile_hll(sh, modinfos, num_modinfos);
+		if (retval < 0) {
+			ERR(sh, "Failed to compile hll files into cil files.\n");
+			goto cleanup;
+		}
+
+		retval = semanage_get_cil_paths(sh, modinfos, num_modinfos, &mod_filenames);
+		if (retval < 0)
+			goto cleanup;
+
+		retval = semanage_verify_modules(sh, mod_filenames, num_modinfos);
 		if (retval < 0)
 			goto cleanup;
 
@@ -734,7 +1094,7 @@
 			cil_set_handle_unknown(cildb, sh->conf->handle_unknown);
 		}
 
-		retval = semanage_load_files(sh, cildb, mod_filenames, num_modfiles);
+		retval = semanage_load_files(sh, cildb, mod_filenames, num_modinfos);
 		if (retval < 0) {
 			goto cleanup;
 		}
@@ -885,8 +1245,13 @@
 		retval = semanage_install_sandbox(sh);
 	}
 
-      cleanup:
-	for (i = 0; mod_filenames != NULL && i < num_modfiles; i++) {
+cleanup:
+	for (i = 0; i < num_modinfos; i++) {
+		semanage_module_info_destroy(sh, &modinfos[i]);
+	}
+	free(modinfos);
+
+	for (i = 0; mod_filenames != NULL && i < num_modinfos; i++) {
 		free(mod_filenames[i]);
 	}
 
@@ -989,16 +1354,22 @@
 	char *separator;
 
 	if ((in_fd = open(install_filename, O_RDONLY)) == -1) {
-		return -1;
+		ERR(sh, "Unable to open %s: %s\n", install_filename, strerror(errno));
+		retval = -1;
+		goto cleanup;
 	}
 
 	if ((data_len = map_file(sh, in_fd, &data, &compressed)) <= 0) {
+		ERR(sh, "Unable to read file %s\n", install_filename);
+		retval = -1;
 		goto cleanup;
 	}
 
 	path = strdup(install_filename);
 	if (path == NULL) {
-		return -1;
+		ERR(sh, "No memory available for strdup.\n");
+		retval = -1;
+		goto cleanup;
 	}
 
 	filename = basename(path);
@@ -1007,6 +1378,7 @@
 		separator = strrchr(filename, '.');
 		if (separator == NULL) {
 			ERR(sh, "Compressed module does not have a valid extension.");
+			retval = -1;
 			goto cleanup;
 		}
 		*separator = '\0';
@@ -1015,6 +1387,7 @@
 	separator = strrchr(filename, '.');
 	if (separator == NULL) {
 		ERR(sh, "Module does not have a valid extension.");
+		retval = -1;
 		goto cleanup;
 	}
 	*separator = '\0';
@@ -1023,8 +1396,10 @@
 
 	retval = semanage_direct_install(sh, data, data_len, filename, lang_ext);
 
-      cleanup:
-	close(in_fd);
+cleanup:
+	if (in_fd != -1) {
+		close(in_fd);
+	}
 	if (data_len > 0) munmap(data, data_len);
 	free(path);
 
@@ -1917,6 +2292,7 @@
 
 	int status = 0;
 	int ret = 0;
+	int type;
 
 	char path[PATH_MAX];
 
@@ -1931,6 +2307,7 @@
 	/* validate module info */
 	ret = semanage_module_info_validate(modinfo);
 	if (ret != 0) {
+		ERR(sh, "%s failed module validation.\n", modinfo->name);
 		status = -2;
 		goto cleanup;
 	}
@@ -1979,23 +2356,51 @@
 	}
 
 	/* install module source file */
+	if (!strcasecmp(modinfo->lang_ext, "cil")) {
+		type = SEMANAGE_MODULE_PATH_CIL;
+	} else {
+		type = SEMANAGE_MODULE_PATH_HLL;
+	}
 	ret = semanage_module_get_path(
 			sh,
 			modinfo,
-			SEMANAGE_MODULE_PATH_HLL,
+			type,
 			path,
 			sizeof(path));
 	if (ret != 0) {
 		status = -3;
 		goto cleanup;
 	}
-	
+
 	ret = bzip(sh, path, data, data_len);
 	if (ret <= 0) {
 		ERR(sh, "Error while writing to %s.", path);
 		status = -3;
 		goto cleanup;
 	}
+	
+	/* if this is an HLL, delete the CIL cache if it exists so it will get recompiled */
+	if (type == SEMANAGE_MODULE_PATH_HLL) {
+		ret = semanage_module_get_path(
+				sh,
+				modinfo,
+				SEMANAGE_MODULE_PATH_CIL,
+				path,
+				sizeof(path));
+		if (ret != 0) {
+			status = -3;
+			goto cleanup;
+		}
+
+		if (access(path, F_OK) == 0) {
+			ret = unlink(path);
+			if (ret != 0) {
+				ERR(sh, "Error while removing cached CIL file %s: %s", path, strerror(errno));
+				status = -3;
+				goto cleanup;
+			}
+		}
+	}
 
 cleanup:
 	semanage_module_key_destroy(sh, &higher_key);
diff --git a/libsemanage/src/handle.c b/libsemanage/src/handle.c
index 7d808ed..e9360a2 100644
--- a/libsemanage/src/handle.c
+++ b/libsemanage/src/handle.c
@@ -23,6 +23,7 @@
 
 #include <selinux/selinux.h>
 
+#include <ctype.h>
 #include <stdarg.h>
 #include <assert.h>
 #include <stdlib.h>
@@ -129,6 +130,59 @@
 	return;
 }
 
+int semanage_get_hll_compiler_path(semanage_handle_t *sh,
+				char *lang_ext,
+				char **compiler_path)
+{
+	assert(sh != NULL);
+	assert(lang_ext != NULL);
+
+	int i;
+	int status = 0;
+	int num_printed = 0;
+	size_t len;
+	char *compiler = NULL;
+	char *lower_lang_ext = NULL;
+
+	lower_lang_ext = strdup(lang_ext);
+	if (lower_lang_ext == NULL) {
+		ERR(sh, "Could not create copy of lang_ext. Out of memory.\n");
+		status = -1;
+		goto cleanup;
+	}
+	/* Set lang_ext to lowercase in case a file with a mixed case extension was passed to libsemanage */
+	for (i = 0; lower_lang_ext[i] != '\0'; i++) {
+		lower_lang_ext[i] = tolower(lower_lang_ext[i]);
+	}
+
+	len = strlen(sh->conf->compiler_directory_path) + strlen("/") + strlen(lower_lang_ext) + 1;
+
+	compiler = malloc(len * sizeof(*compiler));
+	if (compiler == NULL) {
+		ERR(sh, "Error allocating space for compiler path.");
+		status = -1;
+		goto cleanup;
+	}
+
+	num_printed = snprintf(compiler, len, "%s/%s", sh->conf->compiler_directory_path, lower_lang_ext);
+	if (num_printed < 0 || (int)num_printed >= (int)len) {
+		ERR(sh, "Error creating compiler path.");
+		status = -1;
+		goto cleanup;
+	}
+
+	*compiler_path = compiler;
+	status = 0;
+
+cleanup:
+	free(lower_lang_ext);
+	if (status != 0) {
+		free(compiler);
+	}
+
+	return status;
+}
+
 void semanage_set_create_store(semanage_handle_t * sh, int create_store)
 {
 
@@ -166,6 +220,19 @@
 	sepol_set_preserve_tunables(sh->sepolh, preserve_tunables);
 }
 
+int semanage_get_ignore_module_cache(semanage_handle_t *sh)
+{
+	assert(sh != NULL);
+	return sh->conf->ignore_module_cache;
+}
+
+void semanage_set_ignore_module_cache(semanage_handle_t *sh,
+				    int ignore_module_cache)
+{
+	assert(sh != NULL);
+	sh->conf->ignore_module_cache = ignore_module_cache;
+}
+
 void semanage_set_check_contexts(semanage_handle_t * sh, int do_check_contexts)
 {
 
diff --git a/libsemanage/src/libsemanage.map b/libsemanage/src/libsemanage.map
index 5fa1783..79b42ac 100644
--- a/libsemanage/src/libsemanage.map
+++ b/libsemanage/src/libsemanage.map
@@ -30,6 +30,9 @@
 LIBSEMANAGE_1.1 {
   global:
 	  semanage_module_install;
+	  semanage_get_hll_compiler_path;
+	  semanage_get_ignore_module_cache;
+	  semanage_set_ignore_module_cache;
 	  semanage_get_default_priority;
 	  semanage_set_default_priority;
 	  semanage_module_info_create;
diff --git a/libsemanage/src/modules.c b/libsemanage/src/modules.c
index 4d4d60a..d0297fe 100644
--- a/libsemanage/src/modules.c
+++ b/libsemanage/src/modules.c
@@ -441,6 +441,7 @@
 
 	tmp = strdup(name);
 	if (!tmp) {
+		ERR(sh, "No memory available for strdup");
 		return -1;
 	}
 
@@ -471,6 +472,7 @@
 
 	tmp = strdup(lang_ext);
 	if (!tmp) {
+		ERR(sh, "No memory available for strdup");
 		return -1;
 	}
 
@@ -580,49 +582,7 @@
 			}
 			break;
 		case SEMANAGE_MODULE_PATH_HLL:
-			/* verify priority, name, and ext */
-			ret = semanage_module_validate_lang_ext(modinfo->lang_ext);
-			if (ret < 0) {
-				errno = 0;
-				ERR(sh,
-				    "Language extensions %s is invalid.",
-				    modinfo->lang_ext);
-				status = -1;
-				goto cleanup;
-			}
-
-			ret = semanage_module_validate_priority(modinfo->priority);
-			if (ret < 0) {
-				errno = 0;
-				ERR(sh,
-				    "Priority %d is invalid.",
-				    modinfo->priority);
-				status = -1;
-				goto cleanup;
-			}
-
-			ret = semanage_module_validate_name(modinfo->name);
-			if (ret < 0) {
-				errno = 0;
-				ERR(sh, "Name %s is invalid.", modinfo->name);
-				status = -1;
-				goto cleanup;
-			}
-
-			ret = snprintf(path,
-				       len,
-				       "%s/%03u/%s/%s.%s",
-				       modules_path,
-				       modinfo->priority,
-				       modinfo->name,
-				       modinfo->name,
-				       modinfo->lang_ext);
-			if (ret < 0 || (size_t)ret >= len) {
-				ERR(sh, "Unable to compose hll path.");
-				status = -1;
-				goto cleanup;
-			}
-			break;
+			if (file == NULL) file = "hll";
 		case SEMANAGE_MODULE_PATH_CIL:
 			if (file == NULL) file = "cil";
 		case SEMANAGE_MODULE_PATH_LANG_EXT:
@@ -783,6 +743,7 @@
 
 	tmp = strdup(name);
 	if (tmp == NULL) {
+		ERR(sh, "No memory available for strdup");
 		status = -1;
 		goto cleanup;
 	}
diff --git a/libsemanage/src/semanage_conf.h b/libsemanage/src/semanage_conf.h
index 4a16250..14ea749 100644
--- a/libsemanage/src/semanage_conf.h
+++ b/libsemanage/src/semanage_conf.h
@@ -32,6 +32,7 @@
 typedef struct semanage_conf {
 	enum semanage_connect_type store_type;
 	char *store_path;	/* used for both socket path and policy dir */
+	char *compiler_directory_path;
 	int server_port;
 	int policyvers;		/* version for server generated policies */
 	int target_platform;
@@ -44,6 +45,7 @@
 	mode_t file_mode;
 	int bzip_blocksize;
 	int bzip_small;
+	int ignore_module_cache;
 	char *ignoredirs;	/* ";" separated of list for genhomedircon to ignore */
 	struct external_prog *load_policy;
 	struct external_prog *setfiles;
diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c
index a202ae8..806a0e6 100644
--- a/libsemanage/src/semanage_store.c
+++ b/libsemanage/src/semanage_store.c
@@ -977,7 +977,7 @@
 	return status;
 }
 
-/* qsort comparison function for semanage_get_modules_names. */
+/* qsort comparison function for semanage_get_active_modules. */
 static int semanage_get_active_modules_cmp(const void *a, const void *b)
 {
 	semanage_module_info_t *aa = (semanage_module_info_t *)a;
@@ -986,48 +986,30 @@
 	return strcmp(aa->name, bb->name);
 }
 
-int semanage_get_modules_names(semanage_handle_t * sh,
-				char *** filenames,
-				int *len)
+int semanage_get_cil_paths(semanage_handle_t * sh,
+				semanage_module_info_t *modinfos,
+				int num_modinfos,
+				char *** filenames)
 {
-	semanage_module_info_t *modinfos = NULL;
-	enum semanage_module_path_type type;
 	char path[PATH_MAX];
+	char **names = NULL;
 
 	int ret;
 	int status = 0;
-
 	int i = 0;
 
-	*filenames = NULL;
-	*len = 0;
-
-	ret = semanage_get_active_modules(sh, &modinfos, len);
-	if (ret != 0) {
+	names = calloc(num_modinfos, sizeof(*names));
+	if (names == NULL) {
+		ERR(sh, "Error allocating space for filenames.");
 		status = -1;
 		goto cleanup;
 	}
 
-	if (*len == 0) {
-		goto cleanup;
-	}
-
-	(*filenames) = calloc(*len, sizeof(char *));
-	if ((*filenames) == NULL) {
-		ERR(sh, "Error allocating space for filenames.");
-	}
-
-	for (i = 0; i < *len; i++) {
-		if (!strcasecmp(modinfos[i].lang_ext, "cil")) {
-			type = SEMANAGE_MODULE_PATH_HLL;
-		} else {
-			type = SEMANAGE_MODULE_PATH_CIL;
-		}
-
+	for (i = 0; i < num_modinfos; i++) {
 		ret = semanage_module_get_path(
 				sh,
 				&modinfos[i],
-				type,
+				SEMANAGE_MODULE_PATH_CIL,
 				path,
 				sizeof(path));
 		if (ret != 0) {
@@ -1035,24 +1017,22 @@
 			goto cleanup;
 		}
 
-		(*filenames)[i] = strdup(path);
-		if ((*filenames)[i] == NULL) {
+		names[i] = strdup(path);
+
+		if (names[i] == NULL) {
 			status = -1;
 			goto cleanup;
 		}
 	}
 
 cleanup:
-	for (i = 0; i < *len; i++) {
-		semanage_module_info_destroy(sh, &modinfos[i]);
-	}
-	free(modinfos);
-
 	if (status != 0) {
-		for (i = 0; i < *len; i++) {
-			free(filenames[i]);
+		for (i = 0; i < num_modinfos; i++) {
+			free(names[i]);
 		}
-		free(*filenames);
+		free(names);
+	} else {
+		*filenames = names;
 	}
 
 	return status;
diff --git a/libsemanage/src/semanage_store.h b/libsemanage/src/semanage_store.h
index 5bdc8f3..c9b00d4 100644
--- a/libsemanage/src/semanage_store.h
+++ b/libsemanage/src/semanage_store.h
@@ -106,8 +106,8 @@
 
 int semanage_make_final(semanage_handle_t * sh);
 
-int semanage_get_modules_names(semanage_handle_t * sh,
-			       char ***filenames, int *len);
+int semanage_get_cil_paths(semanage_handle_t * sh, semanage_module_info_t *modinfos,
+			       int len, char ***filenames);
 
 int semanage_get_active_modules(semanage_handle_t *sh,
 			       semanage_module_info_t **modinfo, int *num_modules);