#ifndef _LVE_CTL_H_

#define _LVE_CTL_H_

#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#ifndef _LVE_TYPE_H_
#include "lve-type.h"
#endif

#define LIBLVE_API_MAJOR 1
#define LIBLVE_API_MINOR 5

#define SIZEOF_LIBLVE (sizeof(void *) + sizeof(void *) + sizeof(int))
struct liblve;

/**
 * initializes and create instance of LVE
 * args
 *   allocator - pointer to function to allocate memory
 * returns
 *    NULL on error, errno will be set. 
 *    errno will be EINVAL if wrong version of library is used
 *    liblve otherwise
 */
struct liblve *init_lve(liblve_alloc alloc, liblve_free free);

static inline struct liblve *init_lve_generic()
{
	return init_lve(malloc, free);
}

/**
 * destroy lve library instance
 * args: 
 *   lve = instantiated liblive instance
 * return 0 on success
 *        negative number on error. errno will be set
 */
int destroy_lve(struct liblve *lve);

/**
 * enter a light virutal environment
 * args:
 * lve = fully initialized liblve instance
 * lve_id = id associated with LVE
 * cookie = pointer to cookie, which is returned if the task
 * is correctly migrated into LVE and used to exit from this LVE
 * return codes:
 * 0 = on success, negative number means error:
 * -EPERM - don't have permission to call, or called from outside root LVE
 * -ENOMEM - there's not enough memory to allocate a new LVE
 * -EFAULT - cookie is a bad pointer
 */
int lve_enter_flags(struct liblve *lve,
		    uint32_t lve_id, uint32_t *cookie,
		    enum liblve_enter_flags flags);

/**
 * enter lve namespace and get a copy of the original fs
 * args:
 * lve = fully initialized liblve instance
 * return codes:
 * -EINVAL - the caller hasn't entered an lve
 */
int lve_enter_fs(struct liblve *lve);


/**
 * exit from virtual environment, same as lve_leave
 * args:
 * lve = fully init liblve instance
 * cookie - pointer to cookie returned from lve_enter
 * return codes:
 * 0 = none error, all less zero is errors:
 * -ESRCH = task not in virutal environment
 * -EFAULT = bad cookie pointer
 * -EINVAL = cookie not match to stored in context
 */
int lve_exit(struct liblve *lve, uint32_t *cookie);

int lve_setup_enter(struct liblve *lve, uint32_t ve_id,
		    struct liblve_settings *set,
		    uint32_t *cookie, enum liblve_enter_flags flags);

/**
 * @brief Checks whether a current task has reached its limits.
 *
 * This call retrieve set of liblve_ve_fails flags, each of them
 * represents resource which allocation failed. This flags combined
 * via bitwise or returns through failmask parameter. Flags are
 * cleared after this call.
 *
 * @param lve - fully initialized liblve instance.
 * @param failmask - pointer to variable where fail mask will be stored.
 * @retval 0 success.
 * @retval -ESRCH task not in virutal environment.
 * @retval -EFAULT bad failmask pointer.
 */
int lve_check_fault(struct liblve *lve, uint32_t * failmask);

#ifdef LVE_DEPRICATED
#warning you need remove old functions
static inline int lve_is_available(void)
{
	struct liblve *lve;

	lve = init_lve(malloc, free);
	if (lve == NULL)
		return 0;
	destroy_lve(lve);

	return 1;
}

static inline int lve_instance_init(struct liblve *lve)
{
	struct liblve *tmp;

	if (lve == NULL)
		return SIZEOF_LIBLVE;

	tmp = init_lve(malloc, NULL);
	if (tmp == NULL)
		return -1;
	memcpy(lve, tmp, SIZEOF_LIBLVE);
	free(tmp);

	return 0;
}

static inline int lve_instance_destroy(struct liblve *lve)
{
	return destroy_lve(lve);
}
#define __unused	__attribute__((unused))
static inline int lve_enter(struct liblve *lve,
				uint32_t ve_id, __unused int32_t uid,
				__unused int32_t gid, uint32_t *cookie)
{
	return lve_enter_flags(lve, ve_id, cookie, 0);
}

static inline int lve_leave(struct liblve *lib, uint32_t *cookie)
{
	return lve_exit(lib, cookie);
}
#endif

/**
 * Return 1 if process in lve context
 * return
 * 1 -  process is in lve context
 * 0 - process is not in lve context
 */
int is_in_lve(struct liblve *lve);


/**
 * return actual api version used to communicate with kernel
 * in format major << 16 | minor
 */
uint32_t lve_kapi_ver(struct liblve *lve);

/**
 * return actual liblve api version used to communicate with
 * userland application
 * return in format major << 16 | minor
 */
uint32_t lve_get_api_version(void);

#ifdef LVE_ADMIN


/**
 * create custom configured virtual enviroment
 * args:
 * ve_id = id associated with VE
 */
int lve_create(struct liblve *lve, uint32_t lve_id);

/**
 * destroy configured virtual environment
 * args:
 * ve_id = id associated with VE
 */
int lve_destroy(struct liblve *lve, uint32_t lve_id);

/**
 * adjust parameters for virtual environment
 * args:
 * ve_id = id associated with VE
 */
int lve_setup(struct liblve *lve, uint32_t lve_id,
	      struct liblve_settings *set);

/**
 * flush context's from kernel
 * args
 * all == true - is need all context flushed, or only default
 *               configured
 * return
 *
 */
int lve_flush(struct liblve *lve, int all);

/**
 * get info about context
 * args
 * ve_id = id associated with VE
 * cpu - pointer to return CPU power.
 * io - pointer to IO priority.
 * return
 * 0 - OK
 * any negative value say error is hit.
 */
int lve_info(struct liblve *lve, uint32_t ve_id, struct liblve_info *set);


int lve_setup_flags(struct liblve *lve, uint32_t ve_id, enum liblve_ve_flags);

/** 
 * migrate existent process into container
 *
 * args
 * ve_id = container id to migrate
 * pid = process id to migrate
 * return
 * 0 - OK
 * any negative value say error is hit.
 */
int lve_enter_pid(struct liblve *lve, uint32_t ve_id, pid_t pid);

/** 
 * migrate existent process into container with flags
 *
 * args
 * ve_id = container id to migrate
 * pid = process id to migrate
 * flags = flag set, see lve_enter
 * return
 *   0 - OK
 * < 0 - for -errno.
 */
int lve_enter_pid_flags(struct liblve *lve, uint32_t ve_id,
		        pid_t pid, uint32_t flags);

/** 
 * migrate existent process from container
 *
 * args
 * pid = process id to migrate
 * return
 * 0 - OK
 * any negative value say error is hit.
 */
int lve_leave_pid(struct liblve *lve, pid_t pid);

/**
 * Set up the lve module
 * returns
 * 0 on success
 * -errno on error
 */
int lve_start(struct liblve *lve);

/**
 * Set fail injection place
 */

int lve_set_fail_val(struct liblve *lve, uint32_t fail_val);

/**
 * enter lve namespace and get the ORIGINAL fs
 * any changes to cwd/root will be applied to all
 * following namespace guests
 * args:
 * lve = fully initialized liblve instance
 * \retval
 * -EPERM - the call is not permitted
 * -EINVAL - the caller hasn't entered an lve
 */
int lve_setup_fs(struct liblve *lve);

/**
 * set lve root mount tree.
 * args
 * lve = fully initialized liblve instance
 * lve_id = lve identiferer to be root changed
 *
 * \retval
*/
int lve_set_root(struct liblve *lve, uint32_t lve_id, const char *root);

/**
 * take current NS and FS settrigs as default for lve.
 * args
 * lve = fully initialized liblve instance
 * lve_id = lve identifer to need assign an NS
 * \retval
 *
 */
int lve_assign_ns(struct liblve *lve, uint32_t lve_id);

/**
 * set lve global parameter value
 * args
 * lve = fully initialized liblve instance
 * param parameter id
 * val = parameter value
 * \retval
 */
int lve_set_global_param(struct liblve *lve,
		enum liblve_global_params param, uint64_t val);

/**
 * get lve global parameter value
 * args
 * lve = fully initialized liblve instance
 * param parameter id
 * val = parameter value
 * \retval
 */
int lve_get_global_param(struct liblve *lve,
		enum liblve_global_params param, uint64_t *val);

/**
 * get lve info by task pid
 * args
 * lve = fully initialized liblve instance
 * pid = task PID
 * info = pointer where to put info
 * \retval
 */
int lve_get_pid_info(struct liblve *lve, pid_t pid, struct liblve_pid_info *info);

/**
 * TCP port bind control
 * args
 * lve = fully initialized liblve instance
 * 
 * \retval
 */
/* helpers */
int lve_net_port_add(struct liblve *lve, uint32_t lve_id, uint16_t port, bool policy);
int lve_net_port_del(struct liblve *lve, uint32_t lve_id, uint16_t port);
int lve_net_port_def(struct liblve *lve, uint32_t lve_id, bool policy);


/**
 * set network limits
 * args
 * lve = fully initialized liblve instance
 * net_limits = uid/port pair
 * \retval
 */
int lve_set_net_limits(struct liblve *lve, uint32_t lve_id,
		       struct liblve_net_limits *net_limits);


/**
 * control freezer
 * args
 * lve = fully initialized liblve instance
 * fc = arguments passed to the freezer
 * \retval
 */
int lve_freezer_control(struct liblve *lve, uint32_t lve_id,
                          struct liblve_freezer_control *fc);

/**
 * create 2nd level lvp
 * args
 * lve = fully initialized liblve instance
 * lvp_id = lvp identifier
 */
int lve_lvp_create(struct liblve *lve, uint32_t lvp_id);

/**
 * create a nested LVP under a parent LVP
 * args
 * lve = fully initialized liblve instance
 * lvp_id = new lvp identifier
 * parent_lvp_id = parent lvp identifier (0 for root)
 */
int lve_lvp_create2(struct liblve *lve, uint32_t lvp_id, uint32_t parent_lvp_id);

/**
 * adjust parameters for virtual environment
 * args:
 * lve = initialized liblve entity
 * set = default parameters settings
 * lvp_id = id associated with resseler
 */
int lve_lvp_setup(struct liblve *lve, uint32_t lvp_id, enum lvp_set_type op,
		  struct liblve_settings *set);

/**
 * destroy 2nd level lvp
 * args
 * lve = fully initialized liblve instance
 * lvp_id - lvp identifier
 */
int lve_lvp_destroy(struct liblve *lve, uint32_t lvp_id);

/* create a mapping between lve and lvp */
int lve_lvp_map(struct liblve *lve, uint32_t lve_id, uint32_t lvp_id);

/** 
 * move lvp to the different reseller
 */
int lve_lvp_move(struct liblve *lve, uint32_t lve_id, uint32_t lvp_id);

/** 
 * read lvp limits
 */
int lve_lvp_info(struct liblve *lve, uint32_t lvp_id, enum lvp_set_type op,
		struct liblve_info *set);
#endif

struct passwd;

/**
 * CageFS definitions
 */

#include <sys/types.h>
#include <pwd.h>

#define SECURELVE_JAIL "/usr/share/cagefs-skeleton"
#define SECURELVE_BASEDIR "/var/cagefs/"
#define SECURELVE_SHELL "/usr/sbin/securelve_sh" /* deprecated */
#define SECURELVE_ETC_MP_FILE "/etc/cagefs/cagefs.mp"
#define SECURELVE_MP_FILE "/usr/share/cagefs/cagefs.mp.work" /* deprecated */

#define SECURELVE_CONFIG_DIR "/etc/cagefs"
#define SECURELVE_MIN_UID 500
#define SECURELVE_MIN_UID_FILENAME "/etc/cagefs/cagefs.min.uid"

#define HOME_REGEX_FILE "/etc/cagefs/cagefs.base.home.dirs"
#define DISABLE_HOME_DIRS_SEARCH "/etc/cagefs/disable.home.dirs.search"
#define DEV_SHM_OPTIONS "/etc/cagefs/dev.shm.options"

/* Max count of regular expressions */
#define MAX_REXPS 100

#define DISABLE_ETCFS "/etc/cagefs/etc.safe/disable.etcfs"
#define PGSQL_SOCKET_CFG "/usr/share/cagefs/pgsql.socket.name"
#define MYSQL_SOCKET_CFG "/usr/share/cagefs/mysql.socket.name"

#define CL_SOLO "/etc/cloudlinux-edition-solo"
#define CL_CONTAINER "/etc/cloudlinux-container"

#define FEATURE_AVAIL_FLAG_DISABLE_RESTRICT_CAPS "/opt/cloudlinux/flags/available-flags.d/disable-cagefs-restricted-caps.flag"
#define FEATURE_ENABLE_FLAG_DISABLE_RESTRICT_CAPS "/opt/cloudlinux/flags/enabled-flags.d/disable-cagefs-restricted-caps.flag"
#define FEATURE_AVAIL_FLAG_DISABLE_NO_NEW_PRIVS "/opt/cloudlinux/flags/available-flags.d/disable-cagefs-no-new-privs.flag"
#define FEATURE_ENABLE_FLAG_DISABLE_NO_NEW_PRIVS "/opt/cloudlinux/flags/enabled-flags.d/disable-cagefs-no-new-privs.flag"

/*
 Function reads MIN UID value from file
 Returns -1 if error has occured
*/
int read_min_uid(unsigned int *min_uid, char *error_str);

/**
 * puts user in CageFS (jail)
 *  return 1 if succesful, -1 if error, 0 if CageFS is disabled
 */
int jail(struct passwd *pw, char * error_str);
int lve_jail(struct passwd *pw, char * error_str);
int lve_jail_uid(struct passwd *pw, unsigned int min_uid, char *error_str);

/**
 * A variant of the 'jail' function, but with additional enhancements:
 * - allows LVE ID an to be passed as an argument, i.e. not be equal to user's UID.
 * - allows flags to be passed to modify the behavior of the jail.
 */
int jail_enhanced(struct passwd * pw, uint32_t lve_id, uint32_t flags, char *error_str);

/**
 * setup namespace for CageFS (jail)
 *  return 1 if succesful, -1 if error, 0 if CageFS is disabled
 */
int lve_namespace_setup(uint32_t lve_id);

/*
  mount personal user's directories in CageFS
*/
int mount_jail(const char *homedir, const char *username, const char *basedir, const char *jaildir, char *error_str, uid_t uid, gid_t gid);

/*
  helper function to strip slash in the end of path
*/
void strip_slash(char *str);

// Returns zero on success, or -1 if an error occurred
int make_dir(const char *target, mode_t mode, int make_parents, char *error_str);

int print_error(char *str, int err, const char *format, ...);

#endif
