All pastes #81081 Raw Edit

Miscellany

public c v1 · immutable
·published 2006-07-07 03:14 UTC
rendered paste body
#include "settings.h"extern int errno;#include <time.h>/* private interface methods start*/static gboolean copy_file(const gchar* source, const gchar* destination) ;static gboolean jail_new_from_file(const gchar* dir, gchar* canonical_name, struct jail** out) ;static gboolean jail_is_assigned(GKeyFile* settings, int uid);static int settings_get_free_jail(GKeyFile* settings);static gboolean settings_set_jail_uid(GKeyFile* settings, const gchar* jail_name, int uid);/* private interface methods end */// convert from host:ip:port to ip:portstatic gchar* get_host(gchar* group_host) {	return group_host+strlen("host:");}// convert from enum to charenum hostaction char2enum(gchar* action) {	if (strcasecmp(action, "deny") == 0)		return DENY;	else if(strcasecmp(action, "allow") == 0)		return ALLOW;	else if(strcasecmp(action, "ask") == 0)		return ASK;	else if(strcasecmp(action, "allowentry") == 0)		return ALLOWENTRY;	else 		return ERROR;}// convert from char to enum gchar* enum2char(enum hostaction action) {	if (action == DENY)		return "deny";	else if(action == ALLOW)		return "allow";	else if(action == ASK)		return "ask";	else if(action == ALLOWENTRY)		return "allowentry";	else		return "error";}/* taken from gnucash and slightly modified */GKeyFile *settings_load_from_file (const gchar * filename, gboolean ignore_error,  gboolean return_empty_struct){	GKeyFile *key_file;	GError *error = NULL;	g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "settings_load_from_file ENTER\n");		if (!filename)		return FALSE;	if (!g_file_test (filename, G_FILE_TEST_EXISTS))		return NULL;	key_file = g_key_file_new ();	if (!key_file)		return NULL;	if (g_key_file_load_from_file(key_file, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {		g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "settings_load_from_file LEAVE SUCCESS\n");		return key_file;	}	/* An error occurred */	if (!return_empty_struct) {		g_key_file_free (key_file);		key_file = NULL;	}	if (!ignore_error)		g_warning ("Unable to read file %s: %s\n", filename, error->message);	g_error_free (error);		g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "settings_load_from_file LEAVE FAILURE\n");		return key_file;}/* taken from gnucash and slightly modified */gboolean settings_save_to_file (const gchar * filename, GKeyFile * key_file,					   GError ** error){	gchar *contents;	gint fd;	gint length;	ssize_t written;	gboolean success = TRUE;	if (filename == NULL || key_file == NULL)		return FALSE;	contents = g_key_file_to_data (key_file, NULL, NULL);	length = strlen (contents);	fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);	if (fd == -1) {		if (error) {			*error = g_error_new (G_FILE_ERROR, g_file_error_from_errno (errno),							 "Cannot open file %s: %s", filename,							 strerror (errno));		}		else {			g_critical ("Cannot open file %s: %s\n", filename,						strerror (errno));		}		g_free (contents);		return FALSE;	}	written = write (fd, contents, length);	if (written == -1) {		success = FALSE;		if (error) {			*error = g_error_new (G_FILE_ERROR, g_file_error_from_errno (errno),							 "Cannot write to file %s: %s", filename,							 strerror (errno));		}		else {			g_critical ("Cannot write to file %s: %s\n", filename,						strerror (errno));		}		close (fd);	}	else if (written != length) {		success = FALSE;		if (error) {			*error = g_error_new (G_FILE_ERROR, g_file_error_from_errno (errno),							 "File %s truncated (provided %d, written %d)",							 filename, length, (int) written);		}		else {			g_critical ("File %s truncated (provided %d, written %d)",						filename, length, (int) written);		}		/* Ignore any error */		close (fd);	}	else if (close (fd) == -1) {		if (error) {			*error = g_error_new (G_FILE_ERROR, g_file_error_from_errno (errno),							 "Close failed for file %s: %s", filename,							 strerror (errno));		}		else {			g_warning ("Close failed for file %s: %s", filename,					   strerror (errno));		}	}	g_free (contents);	return success;} // print the host structure in human readable formvoid host_print(struct host* h) {	g_assert(h != NULL);		gchar** execve = h->execve;	g_print("host_addr:\t%s\n", h->host_addr);	g_print("action:\t%s\n", enum2char(h->action));	g_print("cmd:\t%s\n", h->cmd);	g_print("execve:{");		while (execve && *execve) {		g_print("%s ",*execve);		execve++;	}				g_print("}\n");	g_print("keys:{");		FOREACH_LIST(h->keys, key) {		g_print("%s ", (char*)key->data);	}		g_print("}\n");	}struct host* host_dup(struct host* old) {	struct host* ret = malloc(sizeof (struct host));	if (ret == NULL) 		g_error("no memory left\n");		memset(ret, 0, sizeof(struct host));		// make the new rule COPY OVER	ret->action = old->action;	SAFE_STRNCPY(ret->host_addr, old->host_addr, MAX_HOST);		SAFE_STRNCPY(ret->cmd, old->cmd, MAX_PATH);			if (ret->execve)		ret->execve = g_strdupv(old->execve);			FOREACH_LIST(ret->keys, key) {		ret->keys = g_list_append(ret->keys, key->data);	}				return ret;	}// load policiesvoid policy_new(int main_uid, struct policies* out) {	DIR* proc;	struct dirent* dirp;	struct jail* new_jail;	char default_path[MAX_PATH+1];	int uid = -1;	GError *err = NULL;	out->uid2jailstruct = g_hash_table_new(NULL, NULL);	proc = opendir(out->policy_path);	if (proc == NULL)		g_error("load_policy:could not open policy dir %s:%s\n", out->policy_path, g_strerror(errno));		if (!SAFE_SNPRINTF(default_path, MAX_PATH, "%s/%s", out->policy_path, "default")) {		g_error("policy_new:failure to concatenate paths\n");	} 		if (access(default_path, R_OK)) {		g_error("policy_new:could not find the default policy in %s\n", default_path);	}		// try to load the main policy//	if (jail_new_from_file(out->policy_path, "main",  &new_jail)==FALSE)//		g_error("load_policy:could not main policy dir:%s\n", g_strerror(errno));	//	out->main = new_jail;//	g_hash_table_insert(out->uid2jailstruct, GINT_TO_POINTER(main_uid), new_jail);		while ( (dirp = readdir(proc)) ) {		// load the policy but ignore main and default policies		if (strcmp(dirp->d_name, "..")==0 || strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "default") == 0)			continue;						if (jail_new_from_file(out->policy_path, dirp->d_name,  &new_jail)) {			g_print("loaded jail with canonical namd %s\n", dirp->d_name);						// find which uid this jail is associated with			uid = settings_get_jail_uid(out->settings, dirp->d_name);			if (uid == -1) {				uid = settings_get_free_jail(out->settings);				if (uid == -1) {					g_print("policy_new:there are no free slots for this new jail\n");					continue;									}				g_print("no associate for jail %s will associate with uid %d\n", new_jail->name, uid);								settings_set_jail_uid(out->settings, new_jail->name, uid);								// update the settings file				settings_save_to_file(out->settings_file, out->settings, &err);										if (err)					g_error("policy_new: failed to update settings file\n");						}						g_hash_table_insert(out->uid2jailstruct, GINT_TO_POINTER(uid), new_jail);		}			}	if (proc)		closedir(proc);	return;}// NOT FINISHED AT ALLvoid policy_free(struct policies* in) {	if (in) {		free(in);	}	// clear up the hash table and all the jails}// match the host wildcard with a given uid. if we find it, we return the pointer to the host structure// that describes the policy for that hostgboolean jail_match_policy(const gchar* hostregex, struct jail* jail_in, struct host** host_out) {	pcre* re = NULL;	const char *error = NULL;	gint erroffset;	int rc;	int ovector[30];	struct host* host;		g_assert(jail_in != NULL);	g_assert(hostregex != NULL);			// iterate over all sequentially, and try to match the host via regex	FOREACH_LIST(jail_in->hosts, host_key) {		host = (struct host*)host_key->data;					// compile regex 		re = pcre_compile(host->host_addr, 0, &error, &erroffset, NULL);		if (re == NULL) { 			g_warning("jail_match_policy:%s\n", error?error:"");					return FALSE;		}				rc = pcre_exec(re, NULL, hostregex, strlen(hostregex), 0, 0, ovector, 30);		if (rc >= 0) {			pcre_free(re);			*host_out = host;			return TRUE;		} else if (re)			pcre_free(re);	}				return FALSE;}// free the hostvoid host_free(struct host* h) {	g_assert(h != NULL);	if (h->execve) 		g_strfreev(h->execve);				while (h->keys) {		if (h->keys->data) {			free(h->keys->data);					}		h->keys = h->keys->next;	}		free(h);}// clean up the linked list of host structuresvoid jail_free(struct jail* in){			g_assert(in != NULL);		while (in->hosts) {		if (in->hosts->data) {			host_free((struct host*)in->hosts->data);		}		in->hosts = in->hosts->next;			}	free(in);} struct policy_search_result {struct jail* jail_result; struct host* host_result; gchar* host_addr;} srch;	// callback for g_hash_table_foreach. pretty uglyvoid has_primary_host(gpointer key, gpointer value, gpointer user_data) {	struct jail* haystack = (struct jail*)value;	// current jail that we are lokoing at	struct host* host_result;							// resultant which will point to the host structure found		// we already found it	if (srch.jail_result != NULL)							// this will be called many times		return;										// if search->ret is already filled in, somebody found it before us													if (jail_match_policy(srch.host_addr, haystack, &host_result)) {		srch.jail_result = (host_result->action==ALLOWENTRY)?(haystack):(NULL);	// this will pass the result back to the caller		srch.host_result = host_result;				return;	}		}// given a certain policy, find in which jail resides the primary host addr host_addr// and return the pointer to itgboolean policy_search_entry_point(	struct policies* policy,	gchar* host_regex,									struct jail** jail_result,	struct host** host_result) {			srch.jail_result = NULL;	srch.host_result = NULL;	srch.host_addr = host_regex;	g_hash_table_foreach(policy->uid2jailstruct, has_primary_host, &srch);	*host_result = srch.host_result;	*jail_result = srch.jail_result;		return *jail_result != NULL && *host_result != NULL;}struct jail* policy_get_jail_by_uid(struct policies* policy, int uid) {	return (struct jail*) g_hash_table_lookup(policy->uid2jailstruct, GINT_TO_POINTER(uid));	}// send the host structure in text format to the data stream pointed to by outgboolean host_write_struct(FILE* fout, struct host* target_host) {		GList* key = NULL;	// dump the host params	fprintf(fout, "[host#%s]\n", target_host->host_addr);	fprintf(fout, "action=%s\n", enum2char(target_host->action));	if (target_host->cmd)		fprintf(fout, "cmd=%s\n", target_host->cmd);	if (target_host->execve)		fprintf(fout, "execve=%s\n", g_strjoinv(";", target_host->execve));		key = target_host->keys;	while (key && key->data) {		fprintf(fout, "%s\n", (char*)key->data);		key = key->next;	}	return TRUE;}// take the command field in target_host and substitute away any string of type {key with}// the actual value of the key if it is defined. if not defined, null will be placedgboolean host_cmd_substition(struct host* target_host, gchar** resultant_cmd, ...) {	GList *key_node = target_host->keys;	gchar* s;	gchar* key = NULL;	gchar* value = NULL;	gchar* ret = g_strdup(target_host->cmd);					va_list ap;								// FIRST WE DO SUBSTITUTION ON THE VARGS	va_start(ap, resultant_cmd);	while (s) {		gchar *temp;		s = va_arg(ap, gchar *);		if (!s)			break;						key = str_unpack_key(s);		value = str_unpack_value(s);		if (key == NULL || value == NULL) {			if (key) free(key);			if (value) free(value);			g_warning("host_cmd_substition:malformed substition\n");			continue;		}						// case sensitive string replace		temp = str_replace(ret, key, value);		g_assert(temp != NULL);				// dump the old return string and replace it with the replaced string		free(ret);		free(key);		free(value);				ret = temp;			}    		va_end(ap);			// SECOND WE DO SUBSTITUTION ON THE POLICY FILE PARAMETERS	while (key_node && key_node->data) {		gchar *temp;				key = str_unpack_key(key_node->data);		value = str_unpack_value(key_node->data);		if (key == NULL || value == NULL) {			if (key) free(key);			if (value) free(value);			g_warning("host_cmd_substition:malformed substition\n");			continue;		}						// case sensitive string replace		temp = str_replace(ret, key, value);		g_assert(temp != NULL);				// dump the old return string and replace it with the replaced string		free(ret);		free(key);		free(value);				ret = temp;					key_node = key_node->next;	}				*resultant_cmd = ret;	return TRUE;                                             	}struct host* host_new() {	struct host* ret = malloc(sizeof(struct host));	memset(ret, 0, sizeof(struct host));	return ret;}gboolean host_new_from_settings(GKeyFile* jail_settings,  gchar* host_string, struct host** host_out) {	gchar** keys = NULL, **keys_iter = NULL;	gsize size;	GError* err = NULL;	gchar* string_key = NULL;	gchar** string_list = NULL;		gboolean error_free = TRUE;			g_assert(host_out != NULL);		 	// we will iterate over all the keys to get and save the mapping	keys = keys_iter = g_key_file_get_keys(jail_settings, host_string, &size, &err);	if (keys == NULL) {		g_warning("get_jail_struct:error during g_key_file_get_keys for %s\n", host_string);		return FALSE;	}		// allocate a host structure to hold the resultant	*host_out = (struct host*)malloc(sizeof(struct host));	if (*host_out == NULL)		return FALSE;		memset(*host_out, 0, sizeof(struct host));		// null it	// get the host address	if (!SAFE_STRNCPY((*host_out)->host_addr, get_host(host_string), MAX_HOST))		error_free = FALSE;				while (*keys_iter && error_free == TRUE) {		error_free = FALSE;				// we are not error free until we extract the key		string_key = NULL;		string_key = NULL;				if (strcmp(*keys_iter, "cmd") == 0) {							string_key = g_key_file_get_string(jail_settings, host_string, "cmd", &err);			if (err) break;							if (!SAFE_STRNCPY((*host_out)->cmd, string_key?string_key:"", MAX_HOST)) {				g_free(string_key);				break;										}							g_free(string_key);		} else if (strcmp(*keys_iter, "execve") == 0) {			string_list = g_key_file_get_string_list(jail_settings, host_string, "execve",  &size, &err);			if (err) break;												if (string_list)	// duplicate the list				(*host_out)->execve = g_strdupv(string_list);													g_strfreev(string_list);								} else if (strcmp(*keys_iter, "action") == 0) {			string_key = g_key_file_get_string(jail_settings, host_string, "action", &err);			if (err) break;							(*host_out)->action  = char2enum(string_key);			g_free(string_key);						if ((*host_out)->action == ERROR)				break;							} else {			gchar* string_key = g_key_file_get_string(jail_settings, host_string, *keys_iter, &err);			if (err) break;									// we will collect all the variables that are not a part of the policy defined keys			(*host_out)->keys = g_list_append((*host_out)->keys, g_strdup_printf("%s=%s", *keys_iter, string_key));								g_free(string_key);		}				error_free = TRUE;		keys_iter++;	}		if (error_free == FALSE) {		if (keys) 		g_strfreev(keys);		if (*host_out)	host_free(*host_out);		*host_out = NULL;				g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "host_new_from_settings LEAVE FAILURE\n");						return FALSE;	}			return TRUE;}// extract the jail structure from the filegboolean jail_new_from_file(const gchar* dir, gchar* canonical_name, struct jail** jail_out) {	char jail_path[MAX_PATH+1];	gsize size;	gchar** groups = NULL, **groups_iter = NULL;	GKeyFile* jail_settings = NULL;	struct host* new_host;	gboolean error_free = TRUE;		g_assert(jail_out != NULL);		g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "jail_new_from_file ENTER\n");		*jail_out = (struct jail*)malloc(sizeof(struct jail));	if (*jail_out == NULL) {		g_warning("jail_new_from_file:%s\n", g_strerror(errno));		return FALSE;	}		memset(*jail_out, 0, sizeof(struct jail));					// store the jail canonical name in the jail structure	if (!SAFE_STRNCPY((*jail_out)->name, canonical_name, MAX_PATH)) {		g_warning("jail_new_from_file:could not set canonical_name\n");		free(*jail_out);		*jail_out = NULL;		return FALSE;			}		// get where this policy is located on disk	if (!SAFE_SNPRINTF(jail_path, MAX_PATH, "%s/%s", dir, canonical_name)) {		g_warning("jail_new_from_file:could not concatenate paths\n");		free(*jail_out);		*jail_out = NULL;		return FALSE;	}		// open the policy file	jail_settings  = settings_load_from_file(jail_path, FALSE,  FALSE);	if (jail_settings == NULL) {		free(*jail_out);		*jail_out = NULL;        g_warning("jail_new_from_file:error during open settings %s\n%s\n", jail_path, strerror(errno));		return FALSE;	}					// get all the hosts for the jail	groups_iter = groups = g_key_file_get_groups(jail_settings, &size);	if (groups == NULL) {		free(*jail_out);		*jail_out = NULL;		g_key_file_free(jail_settings);		g_warning("jail_new_from_file:error during g_key_file_get_groups for %s\n", jail_path);		return FALSE;	}		// iterate over all the hosts	while (*groups_iter && error_free == TRUE) {			error_free = FALSE;				if (strstr(*groups_iter, "host#") == *groups_iter) {						if (!host_new_from_settings(jail_settings, *groups_iter, &new_host))				break;														(*jail_out)->hosts = g_list_append((*jail_out)->hosts, new_host);	 	// add it to the jail list																	error_free = TRUE;				}				groups_iter++;			}			if (error_free == FALSE) {		g_warning("jail_new_from_file:error during g_key_get_xxxxx:command for %s\n", jail_path);		jail_free(*jail_out);		*jail_out = NULL;	}		g_strfreev(groups);	g_key_file_free(jail_settings);		g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "jail_new_from_file LEAVE SUCCESS\n");			return error_free;}// make a new jail with either an existing name// or make a jail name which will be of the form <time><uid>struct jail* policy_create_new_jail(struct policies* in) {	char new_jail_path[MAX_PATH+1];	char default_jail_path[MAX_PATH+1];	char new_jail_name[MAX_PATH+1];	int uid;	struct jail* new_jail_struct = NULL;	GError *err = NULL;		g_assert(in != NULL);	g_assert(in->settings != NULL);	// get the user id of a free jail from the jail pool in jail_list	uid = settings_get_free_jail(in->settings );					if (uid == -1) {		g_warning("policy_create_new_jail:could not allocate a new jail\n");		return NULL;	}	// get the default policy path	SAFE_SNPRINTF(default_jail_path, MAX_PATH, "%s/default", in->policy_path); 	// if jail_name is null, creat a new jail name based on time+uid. otherwise,	SAFE_SNPRINTF(new_jail_name, MAX_PATH, "%d-%d", (int)time(NULL), uid);	// associate the new jail name with uid in the jail_mapping table	if (!settings_set_jail_uid(in->settings , new_jail_name, uid)) {		g_warning("policy_create_new_jail:could make new assocciation\n");		return NULL;	}		// craft a path for the new jail	if (!SAFE_SNPRINTF(new_jail_path, MAX_PATH, "%s/%s", in->policy_path, new_jail_name)) {		g_warning("policy_create_new_jail:failure to format new_jail_path\n");		return NULL;	}		// copy the file	if (!copy_file(default_jail_path, new_jail_path))  {		g_warning("policy_create_new_jail:failed to copy file\n");		return NULL;		}			// retrieve the new jail structure from the fiile	if (!jail_new_from_file(in->policy_path, new_jail_name, &new_jail_struct)) {		g_warning("policy_create_new_jail:failed to load the datastructure from the new policy\n");		return NULL;	}			// update the settings file	settings_save_to_file(in->settings_file, in->settings, &err);							if (err) {		g_warning("policy_create_new_jail: failed to update settings file\n");		jail_free(new_jail_struct);		return NULL;	}		// index the new jail struct by uid	g_hash_table_insert(in->uid2jailstruct, GINT_TO_POINTER(uid), new_jail_struct);	return new_jail_struct;	}// add a host to the beginning of the jail associated with a given uid. this updates// the hashes as well gboolean policy_prepend_host(struct policies* policy, struct host* new_host, struct jail** target_jail) {					gboolean ret = FALSE;	FILE* original = NULL; 	FILE* temporary  = NULL;	char buff[4096+1];	size_t in_read = 0;	size_t in_wrote = 0;	char temporary_path[MAX_PATH+1];	char original_path[MAX_PATH+1];	g_assert(target_jail != NULL);	g_assert(new_host != NULL);	g_assert(policy != NULL);		// make the temporary path	if (!SAFE_SNPRINTF(temporary_path, MAX_PATH, "%s/%s_t", policy->policy_path, (*target_jail)->name)) {		g_warning("policy_prepend_host:could not sprintf temporary_path\n");		return FALSE;	}		// make the original path	if (!SAFE_SNPRINTF(original_path, MAX_PATH, "%s/%s", policy->policy_path, (*target_jail)->name)) {		g_warning("policy_prepend_host:could not sprintf original_path\n");		return FALSE;	}		// make sure the file is not there	if (access(temporary_path, R_OK) == 0) {		g_warning("policy_prepend_host:temporary file exists! this is a sign of a big error\n");		return FALSE;	}		// copy to a temporary file	if (!copy_file(original_path, temporary_path)) {		g_warning("policy_prepend_host:could not copy to a temporary file\n");		return FALSE;	}		// kill the original	if (unlink(original_path) == -1) {		g_warning("policy_prepend_host:could not delete outdated jail file:%s\n", g_strerror(errno));		return FALSE;	}			original = fopen(original_path, "a");	temporary = fopen(temporary_path, "r");		if (original == NULL || temporary == NULL) {		if (original) fclose(original);		if (temporary) fclose(temporary);		return FALSE;	}		host_write_struct(original, new_host);		while (!feof(temporary) && !ferror(original) && !ferror(original)) {		in_wrote = 0;			in_read = fread(buff, 1, 4096, temporary);		while (in_wrote != in_read) {				in_wrote = fwrite(buff+in_wrote, 1, in_read-in_wrote, original);		}	}		if (original) fclose(original);	if (temporary)	{		ret = feof(temporary);		fclose(temporary);			}			if (unlink(temporary_path) == -1) {		g_warning("policy_prepend_host:could not delete temporary file:%s\n", g_strerror(errno));		return FALSE; 	}		if (ret == FALSE) {		g_warning("policy_prepend_host:highly inconsistent state!\n");		return FALSE;			}	return policy_reload_jail(policy, target_jail);}// close the jail and open it again. update the hash and write the changes back to *targetgboolean policy_reload_jail(struct policies* policy, struct jail** target) {	int uid = -1;	char jail_name[MAX_PATH+1];		g_assert(policy != NULL);		SAFE_STRNCPY(jail_name, (*target)->name, MAX_PATH);		uid = settings_get_jail_uid(policy->settings, (*target)->name);	if (uid == -1) 		g_error("policy_reload_jail:could not reload a jail that does not exist\n");		g_hash_table_remove(policy->uid2jailstruct, GINT_TO_POINTER(uid));		jail_free(*target);		if (!jail_new_from_file(policy->policy_path, jail_name, target)) {		g_warning("policy_reload_jail:could not read the jail\n");		return FALSE;	}		// update the hash	g_hash_table_insert(policy->uid2jailstruct, GINT_TO_POINTER(uid), *target);	return TRUE;	}// return uid of a free jailstatic int settings_get_free_jail(GKeyFile* settings) {	gchar** jails = NULL;	gchar** jails_iter;	GError *err= NULL;	gsize length;		// get the list of all uids	jails_iter = jails = g_key_file_get_keys(settings, "jail_list",  &length, &err);	if (err) {		g_error("settings_get_free_jail:could not get the list of jails!\n");		g_strfreev(jails);		return -1;	}       	                           	// iterate through all uids and find a free one    		while (*jails_iter) {		// if the jail is not assigned, return the uid		if (jail_is_assigned(settings, atoi(*jails_iter)) == FALSE) {			int ret = atoi(*jails_iter);			// free up the buffer			g_strfreev(jails);			return ret;		}														// move to the next uid									jails_iter++;				}		// free up the buffer	if (jails) g_strfreev(jails);		g_warning("settings_get_free_jail:no free jails remain\n");		return -1;} // path must be MAX_PATH long// returns the length of path stringint settings_get_jail_path(GKeyFile* settings, int uid, gchar* path) {	GError* err = NULL;	gchar str_uid[256];	gchar* ret = NULL;		SAFE_SNPRINTF(str_uid, 256, "%d", uid);			ret = g_key_file_get_string(settings, "jail_list", str_uid, &err);	if (err)		return -1;		if (!SAFE_STRNCPY(path, ret, MAX_PATH)) {		g_warning("settings_get_jail_path:could not save the result\n");	}		return strlen(ret);	}// is there a jail assigned to uid?static gboolean jail_is_assigned(GKeyFile* settings, int uid) {		gsize length;	GError* err = NULL;	gchar** jails = NULL;	gchar** jails_iter = NULL;		// get all the jail canonical names	jails = jails_iter = g_key_file_get_keys(settings, "jail_mapping", &length, &err);	if (*jails == NULL)		return FALSE;		// iterate through all the canonicals	// and see if any of them are mapped to this given uid	// if so, return TRUE	while (*jails_iter) {		if (settings_get_jail_uid(settings, *jails_iter) == uid) {			if (jails) g_strfreev(jails);			return TRUE;		}				jails_iter++;	}		// free up the buffer	if (jails) g_strfreev(jails);	return FALSE;}// find which uid the canonical jail is mapped toint settings_get_jail_uid(GKeyFile* settings, const gchar* jail_name) {	int ret;	GError *err = NULL;	ret = g_key_file_get_integer(settings, "jail_mapping", jail_name, &err);	if (err != NULL)		ret = -1; 	return ret;}// associate a canonical jail name with uidstatic gboolean settings_set_jail_uid(GKeyFile* settings, const gchar* jail_name, int uid) {	g_key_file_set_integer(settings, "jail_mapping", jail_name, uid);			return TRUE;}// copy the filestatic gboolean copy_file(const gchar* source, const gchar* destination) {	FILE* in = fopen(source, "r");	FILE* out = fopen(destination, "a");	char buff[4096+1];	size_t in_read = 0;	size_t in_wrote = 0;	gboolean is_eof;		if (in == NULL || out == NULL) {		if (in) fclose(in);		if (out) fclose(out);		return FALSE;	}		while (!feof(in) && !ferror(in) && !ferror(out)) {		in_wrote = 0;			in_read = fread(buff, 1, 4096, in);		while (in_wrote != in_read) {				in_wrote = fwrite(buff+in_wrote, 1, in_read-in_wrote, out);		}	}	is_eof = feof(in);			// DEBUGGING ASSERT	g_assert(ferror(in) == FALSE);	g_assert(ferror(out) == FALSE);		if (in) fclose(in);	if (out) fclose(out);					return is_eof == TRUE;}gchar* str_pack_key_value(const gchar* key_string, const gchar* value_string){	return g_strdup_printf("%s=%s", key_string, value_string);}// get key from key=value. make sure to free resultantgchar* str_unpack_key(const gchar* key_string){	gchar* eq = NULL;	gchar* ret;	if ((eq = strchr(key_string, '='))) {		ret = malloc(eq - key_string + 1);		if (ret == NULL)			return NULL;				strncpy(ret, key_string, eq - key_string);		ret[eq-key_string] = 0x00;				return ret;	}	return NULL;}// get value from key=value make sure to free resultantgchar* str_unpack_value(const gchar* key_string) {	gchar* eq = NULL;	gchar* ret;	int key_string_length = strlen(key_string);	if ((eq = strchr(key_string, '='))) {		ret = malloc(key_string_length - (key_string - eq));		if (ret == NULL)			return NULL;				strncpy(ret, eq+1, key_string_length - (key_string - eq));		return ret;	}	return NULL;}// replace one instance of from with to in str strgchar* str_replace(const gchar* str, const gchar* from, const gchar* to) {	gchar* resultant = NULL;	int str_length = strlen(str);	int from_length = strlen(from);	int to_length = strlen(to);		gchar* loc_substr = NULL;	 	if ((loc_substr = strstr(str, from))) {		int head_size = loc_substr - str;		int tail_size = str_length - head_size - from_length + 1;				g_assert(str_length + to_length - from_length + 1 > 0);		resultant = (gchar*)malloc(str_length + to_length - from_length + 1);		// copy the beginning of the string		memcpy(resultant, str, head_size);		// copy the replacement		memcpy(head_size + resultant, to, to_length);		// copy the tail (+1 for 0x00)		memcpy(head_size + resultant + to_length, loc_substr + from_length, tail_size);				return resultant;	} else {			return g_strdup(str);	}}