/*
 *  Copyright (C) 2000-2006 SWsoft. All rights reserved.
 *
 *  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; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

#include "list.h"
#include "logger.h"
#include "config.h"
#include "vzerror.h"
#include "util.h"
#include "script.h"
#include "lock.h"
#include "modules.h"
#include "create.h"
#include "destroy.h"

#define VZOSTEMPLATE	"/usr/bin/vzosname"

static char *get_ostemplate_name(char *ostmpl)
{
	FILE *fd;
	char buf[STR_SIZE];
	char *p;
	int status;

	snprintf(buf, sizeof(buf), VZOSTEMPLATE " %s", ostmpl);
	if ((fd = popen(buf, "r")) == NULL) {
		logger(0, errno, "Error in popen(%s)", buf);
		return NULL;
	}
	*buf = 0;
	while((p = fgets(buf, sizeof(buf), fd)) != NULL);
	status = pclose(fd);
	if (WEXITSTATUS(status) || *buf == 0) {
		logger(0, 0, "Unable to get full ostemplate name for %s",
			ostmpl);
		return NULL;
	}
	if ((p = strchr(buf, '\n')))
		*p = 0;
	return strdup(buf);
}

int fs_create(envid_t veid, fs_param *fs, tmpl_param *tmpl, dq_param *dq,
	char *tar_nm)
{
	char tarball[PATH_LEN];
	char tmp_dir[PATH_LEN];
	char buf[PATH_LEN];
	int ret;
	char *arg[2];
	char *env[4];
	int quota = 0;
	
	snprintf(tarball, sizeof(tarball), "%s/%s.tar.gz", fs->tmpl, tar_nm);
	if (!stat_file(tarball)) {
		logger(0, 0, "Cached os template %s not found",	tarball);
		return VZ_PKGSET_NOT_FOUND;
	}
	/* Lock VPS area */
	if (make_dir(fs->private, 0))
		return VZ_FS_NEW_VE_PRVT;
	snprintf(tmp_dir, sizeof(tmp_dir), "%s.tmp", fs->private);
	if (stat_file(tmp_dir)) {
		logger(0, 0, "Warning: Temp dir %s already exists, deleting",
			tmp_dir);
		if (del_dir(tmp_dir)) {
			ret = VZ_FS_NEW_VE_PRVT;
			goto err;
		}
	}
	if (make_dir(tmp_dir, 1)) {
		logger(0, errno, "Unable to create directory %s", tmp_dir);
		ret = VZ_FS_NEW_VE_PRVT;
		goto err;
	}
	if (dq != NULL && 
		dq->enable == YES &&
		dq->diskspace != NULL &&
		dq->diskinodes != NULL)
	{
		if (!quota_ctl(veid, QUOTA_STAT))
			quota_off(veid, 0);
		quota_ctl(veid, QUOTA_DROP);
		quota_init(veid, tmp_dir, dq);
		quota_on(veid, tmp_dir, dq);
		quota = 1;
	}
	arg[0] = VPS_CREATE;
	arg[1] = NULL;
	snprintf(buf, sizeof(buf), "PRIVATE_TEMPLATE=%s", tarball);
	env[0] = strdup(buf);
	snprintf(buf, sizeof(buf), "VE_PRVT=%s", tmp_dir);
	env[1] = strdup(buf);
	env[2] = strdup(ENV_PATH);
	env[3] = NULL;
	ret = run_script(VPS_CREATE, arg, env, 0);
	free_arg(env);
	if (ret)
		goto err;
	if (quota) {
		quota_off(veid, 0);
		quota_set(veid, fs->private, dq);
		quota = 0;
	}
	/* Unlock VPS area */
	rmdir(fs->private);
	if (rename(tmp_dir, fs->private)) {
		logger(0, errno, "Can't rename %s to %s", tmp_dir, fs->private);
		ret = VZ_FS_NEW_VE_PRVT;
	}

err:
	if (ret && quota) {
		quota_off(veid, 0);
		quota_ctl(veid, QUOTA_DROP);
	}
	rmdir(fs->private);
	rmdir(tmp_dir);

	return ret;
}

int vps_create(vps_handler *h, envid_t veid, vps_param *vps_p, vps_param *cmd_p,
	struct mod_action *action)
{
	int ret;
	char tar_nm[256];
	char src[STR_SIZE];
	char dst[STR_SIZE];
	const char *sample_config;
	fs_param *fs = &vps_p->res.fs;
	tmpl_param *tmpl = &vps_p->res.tmpl;
	vps_param *conf_p;
	int cfg_exists;
	char *full_ostmpl;

	get_vps_conf_path(veid, dst, sizeof(dst));
	sample_config = NULL;
	cfg_exists = 0;
	if (stat_file(dst))
		cfg_exists = 1;
	if (cmd_p->opt.config != NULL) {
		snprintf(src, sizeof(src),  VPS_CONF_DIR "ve-%s.conf-sample",
			cmd_p->opt.config);
		if (!stat_file(src)) {
			logger(0, 0, "File %s is not found", src);
			return VZ_CP_CONFIG;
		}
		if (cfg_exists) {
			logger(0, 0, "Warning: VPS config file already exists,"
				" will be rewrited with %s", src);
			unlink(dst);
		}
		sample_config = cmd_p->opt.config;
	} else if (vps_p->opt.config != NULL) {
		snprintf(src, sizeof(src),  VPS_CONF_DIR "ve-%s.conf-sample",
			vps_p->opt.config);
		/* Do not use config if VPS config exists */
		if (!cfg_exists && stat_file(src)) 
			sample_config = vps_p->opt.config;
	}
	if (sample_config != NULL) {
		if (cp_file(dst, src))
			return VZ_CP_CONFIG;
		if ((conf_p = init_vps_param()) == NULL)
			return VZ_RESOURCE_ERROR;
		vps_parse_config(veid, src, conf_p, action);
		merge_vps_param(vps_p, conf_p);
		if (conf_p->opt.origin_sample == NULL)
			cmd_p->opt.origin_sample = strdup(sample_config);
		free_vps_param(conf_p);
	}
	if (check_var(fs->tmpl, "TEMPLATE is not set"))
		return VZ_VE_TMPL_NOTSET;
	if (check_var(fs->private, "VE_PRIVATE is not set"))
		return VZ_VE_PRIVATE_NOTSET;
	if (check_var(fs->root, "VE_ROOT is not set"))
		return VZ_VE_ROOT_NOTSET;
	if (stat_file(fs->private)) {
		logger(0, 0, "Private area already exists in %s", fs->private);
		return VZ_FS_PRVT_AREA_EXIST;
	}
	merge_vps_param(vps_p, cmd_p);
	logger(0, 0, "Creating VPS private area: %s", fs->private);
	if (action != NULL && action->mod_count) {
		ret = mod_setup(h, veid, 0, 0, action, vps_p);
	} else {
		/* Set default ostemplate if not specified */
		if (cmd_p->res.tmpl.ostmpl == NULL &&
			tmpl->ostmpl == NULL &&
			tmpl->def_ostmpl != NULL)
		{
			tmpl->ostmpl = strdup(tmpl->def_ostmpl);
		}
		if (check_var(tmpl->ostmpl, "OS template is not specified"))
			return VZ_VE_PKGSET_NOTSET;
		if (stat_file(VZOSTEMPLATE)) {
			full_ostmpl = get_ostemplate_name(tmpl->ostmpl);
			if (full_ostmpl != NULL) {
				free(tmpl->ostmpl);
				tmpl->ostmpl = full_ostmpl;
			}
		}
		snprintf(tar_nm, sizeof(tar_nm), "cache/%s", tmpl->ostmpl);
		ret = fs_create(veid, fs, tmpl, &vps_p->res.dq, tar_nm);
	}
	if (ret) {
		if (sample_config != NULL)
			unlink(dst);
		vps_destroy_dir(veid, fs->private);
		logger(0, 0, "Creation of VPS private area failed");
		return ret;
	}
	vps_postcreate(veid, &vps_p->res.fs, &vps_p->res.tmpl);
	move_config(veid, DESTR);
	/* store root, private, ostemplate in case default used */
	if (cmd_p->res.fs.root_orig == NULL &&
		fs->root_orig != NULL)
	{
		cmd_p->res.fs.root_orig = strdup(fs->root_orig);
	}
	if (cmd_p->res.fs.private_orig == NULL &&
		fs->private_orig != NULL)
	{
		cmd_p->res.fs.private_orig = strdup(fs->private_orig);
	}
	/* Store full ostemplate name */
	if (tmpl->ostmpl != NULL) {
		if (cmd_p->res.tmpl.ostmpl != NULL)
			free(cmd_p->res.tmpl.ostmpl);
		cmd_p->res.tmpl.ostmpl = strdup(tmpl->ostmpl);
	}
	vps_save_config(veid, dst, cmd_p, vps_p, action);
	logger(0, 0, "VPS private area was created");

	return 0;
}

int vps_postcreate(envid_t veid, fs_param *fs, tmpl_param *tmpl)
{
	char buf[STR_SIZE];
	dist_actions actions;
	char *dist_name;
	char *arg[2];
	char *env[3];
	int ret;
 
	if (check_var(fs->root, "VE_ROOT is not set"))
                return VZ_VE_ROOT_NOTSET;
	dist_name = get_dist_name(tmpl);
	ret = read_dist_actions(dist_name, DIST_DIR, &actions);
	if (dist_name != NULL)
		free(dist_name);
	if (ret)
		return ret;
	if (actions.post_create == NULL) {
		ret = 0;
		goto err;
	}
	ret = fsmount(veid, fs, NULL);
	if (ret)
		goto err;
	arg[0] = actions.post_create;
	arg[1] = NULL;
	snprintf(buf, sizeof(buf), "VE_ROOT=%s", fs->root);
	env[0] = buf;
	env[1] = ENV_PATH;
	env[2] = NULL;
	logger(0, 0, "Performing postcreate actions");
	ret = run_script(actions.post_create, arg, env, 0);
	fsumount(veid, fs->root);
err:
	free_dist_actions(&actions);
	return ret;
}
