#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>

#include "lve-ctl.h"
#include "lve-api.h"

#define MAXCPUUNITS		500000
#define MINCPUUNITS		8

#define LVE_DEV		"/dev/"LVE_DEV_NAME

struct liblve {
	int fd;
};

int lve_is_available(void)
{
	return access(LVE_DEV, R_OK) == 0;
}

static int
lve_cmd_adm(int cmd, uint32_t id, void *data)
{
	int fd, rc;
	struct ve_ioctl vectl;
	uint32_t api_ver;

	fd = open(LVE_DEV, O_RDONLY);
	if (fd == -1)
		return -errno;

	vectl.id = 0;
	vectl.data = NULL;
	api_ver = ioctl(fd, API_VER, &vectl);
	if (api_ver != LVE_API_VERSION(LVE_KMOD_API_MAJOR, LVE_KMOD_API_MINOR)) {
		close(fd);
		fprintf(stderr,"ERROR API version\n");
		return -EINVAL;
	}

	vectl.id = id;
	vectl.data = data;

	rc = ioctl(fd, cmd, &vectl);
	close(fd);

	return rc;
}

static int
lve_cmd(struct liblve *lib, int cmd, uint32_t ve_id, void *data)
{
	int rc;
	struct ve_ioctl vectl;

	vectl.id = ve_id;
	vectl.data = data;

	rc = ioctl(lib->fd, cmd, &vectl);

	return rc;
}

int lve_instance_init(struct liblve *lve)
{
	uint32_t api_ver;

	if (lve == NULL)
		return sizeof(struct liblve);

	lve->fd = open(LVE_DEV, O_RDONLY);
	if (lve->fd == -1)
		return -errno;

	api_ver = lve_cmd(lve, API_VER, 0, NULL);
	if (api_ver != LVE_API_VERSION(LVE_KMOD_API_MAJOR, LVE_KMOD_API_MINOR)) {
		close(lve->fd);
		return -EINVAL;
	}

	return 0;
}

int lve_instance_destroy(struct liblve *lve)
{
	if (lve == NULL)
		return -EINVAL;

	close(lve->fd);

	return 0;
}

int
lve_set_default(int32_t cpu, int32_t io, int32_t maxentry)
{
	struct ve_config cfg;
	unsigned cpulim1024 = (float)cpu * 1024 / 100;

	cfg.cpu = cpulim1024;
	cfg.io = io;
	cfg.enters = maxentry;

	return lve_cmd_adm(DEFAULT_PARAMS, 0, &cfg);
}

#if 0
static int
lve_getgroups(struct ve_enter *cmd, char *user, int32_t gid)
{
	int limit = __sysconf (_SC_NGROUPS_MAX);
	gid_t *groups = NULL;
	struct passwd *pw;
	int32_t uid;
	char *err;

	if (limit == 0) {
	/* No fixed limit on groups.  Pick a starting buffer size.  */
		limit = 16;
	}

	pw = getpwnam(user);
	if (pw == NULL) {
		limit = 0;
		/* try convert from number uid */
		uid = strtol(user, &err, 10);
		if ((*err != '\0') || (errno != 0))
			return -ERANGE;
	} else {
		uid = pw->pw_uid;

		groups = malloc(limit * sizeof (gid_t));
		if (groups == NULL)
			return -ENOMEM;
			
		if (getgrouplist(user, pw->pw_gid, groups, &limit) == -1) {
			free(groups);
			return -errno;
		}
	}

	cmd->uid = uid;
	cmd->grplist = groups;
	cmd->grpsize = limit;
}
#endif

int
lve_enter(struct liblve *lib,
	  uint32_t ve_id, int32_t uid,
	  int32_t gid, uint32_t *cookie)
{
	struct ve_enter cmd;
	int rc;

	if (cookie == NULL)
		return -EINVAL;
#if 0
	if (user != NULL) {
		rc = lve_getgroups(&cmd, user, gid);
		if (rc < 0)
			return rc;
	}
#endif

	cmd.uid = uid;
	cmd.gid = gid;
	cmd.cookie = cookie;
	/* XXX */
	cmd.grp_size = -1;
	if (gid != -1) {
		cmd.grp_list = &gid;
		cmd.grp_size = 1;
	}
	
	rc = lve_cmd(lib, ENTER_VE, ve_id, &cmd);
#if 0
	if (cmd.grplist)
		free(cmd.grplist);
#endif
	return rc;
}

int lve_leave(struct liblve *lib, uint32_t *cookie)
{
	struct ve_leave cmd;

	if (cookie == NULL)
		return -EINVAL;

	cmd.cookie = cookie;
	return lve_cmd(lib, LEAVE_VE, 0, &cmd);
}

int lve_create(uint32_t ve_id)
{
	return lve_cmd_adm(CREATE_VE, ve_id, NULL);
}

int lve_destroy(uint32_t ve_id)
{
	return lve_cmd_adm(DESTROY_VE, ve_id, NULL);
}

int lve_setup(uint32_t ve_id, 
		uint32_t cpu, uint32_t io, int32_t maxentry)
{
	struct ve_config cfg;
	unsigned cpulim1024 = (float)cpu * 1024 / 100;

	cfg.cpu = cpulim1024;
	cfg.io = io;
	cfg.enters = maxentry;

	return lve_cmd_adm(SETUP_VE, ve_id, &cfg);
}

int lve_flush(int all)
{
	struct lve_flush cfg;

	cfg.all = all;
	return lve_cmd_adm(FLUSH_VE, 0, &cfg);
}

int lve_info(uint32_t ve_id, 
		uint32_t *cpu, uint32_t *io)
{
	struct ve_config cfg;
	int rc;

	rc = lve_cmd_adm(INFO_VE, ve_id, &cfg);
	if (rc == 0) {
		*cpu = (cfg.cpu * 100) / 1024;
		*io = cfg.io;
	}
	return rc;
}
