blob: a2bb67812261b5b41281a6b49a944422d9405590 [file] [log] [blame] [raw]
/*
* Copyright (C) 2000-2009, Parallels, Inc. 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 VPS_CREATE LIB_SCRIPTS_DIR "vps-create"
#define VZOSTEMPLATE "/usr/bin/vzosname"
static int vps_postcreate(envid_t veid, vps_res *res);
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(-1, 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(-1, 0, "Unable to get full ostemplate name for %s",
ostmpl);
return NULL;
}
if ((p = strchr(buf, '\n')))
*p = 0;
return strdup(buf);
}
static 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;
int i;
const char *ext[] = { "", ".gz", ".bz2", ".xz", NULL };
const char *errmsg_ext = "[.gz|.bz2|.xz]";
for (i = 0; ext[i] != NULL; i++) {
snprintf(tarball, sizeof(tarball), "%s/%s.tar%s",
fs->tmpl, tar_nm, ext[i]);
logger(1, 0, "Looking for %s", tarball);
if (stat_file(tarball))
break;
}
if (ext[i] == NULL) {
logger(-1, 0, "Cached OS template %s/%s.tar%s not found",
fs->tmpl, tar_nm, errmsg_ext);
return VZ_OSTEMPLATE_NOT_FOUND;
}
/* Lock CT 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(-1, 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(-1, 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) {
if ((ret = quota_off(veid, 0)) != 0)
goto err;
if ((ret = quota_set(veid, fs->private, dq)) != 0)
goto err;
quota = 0;
}
/* Unlock CT area */
rmdir(fs->private);
if (rename(tmp_dir, fs->private)) {
logger(-1, 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 = 0;
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(-1, 0, "File %s is not found", src);
ret = VZ_CP_CONFIG;
goto err;
}
if (cfg_exists) {
logger(-1, 0, "Error: container config file %s "
"already exists, can not use --config "
"option", dst);
ret = VZ_CP_CONFIG; /* FIXME */
goto err;
}
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);
/* Bail out if CONFIGFILE is wrong in /etc/vz/vz.conf */
if (!stat_file(src)) {
logger(-1, errno, "File %s not found", src);
logger(-1, 0, "Fix the value of CONFIGFILE in "
GLOBAL_CFG);
ret = VZ_CP_CONFIG;
goto err;
}
/* Do not use config if CT config exists */
if (!cfg_exists)
sample_config = vps_p->opt.config;
else {
logger(0, 0, "Warning: CT config file already "
"exists, not applying a default config "
"sample.");
logger(0, 0, "It might lead to incomplete CT "
"configuration, you can use --applyconfig "
"to fix.");
}
}
if (sample_config != NULL) {
if (cp_file(dst, src))
{
ret = VZ_CP_CONFIG;
goto err;
}
if ((conf_p = init_vps_param()) == NULL)
{
ret = VZ_RESOURCE_ERROR;
goto err_cfg;
}
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);
}
merge_vps_param(vps_p, cmd_p);
if (check_var(fs->tmpl, "TEMPLATE is not set"))
{
ret = VZ_VE_TMPL_NOTSET;
goto err_cfg;
}
else if (check_var(fs->private, "VE_PRIVATE is not set"))
{
ret = VZ_VE_PRIVATE_NOTSET;
goto err_cfg;
}
else if (check_var(fs->root, "VE_ROOT is not set"))
{
ret = VZ_VE_ROOT_NOTSET;
goto err_cfg;
}
else if (stat_file(fs->private)) {
logger(-1, 0, "Private area already exists in %s", fs->private);
ret = VZ_FS_PRVT_AREA_EXIST;
goto err_cfg;
}
if (cmd_p->res.name.name) {
if ((ret = set_name(veid, cmd_p->res.name.name,
cmd_p->res.name.name)))
goto err_cfg;
}
if (action != NULL && action->mod_count) {
if ((ret = mod_setup(h, veid, 0, 0, action, vps_p)))
goto err_private;
} 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"))
{
ret = VZ_VE_OSTEMPLATE_NOTSET;
goto err_cfg;
}
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);
logger(0, 0, "Creating container private area (%s)",
tmpl->ostmpl);
if ((ret = fs_create(veid, fs, tmpl, &vps_p->res.dq, tar_nm)))
goto err_private;
}
if ((ret = vps_postcreate(veid, &vps_p->res)))
goto err_root;
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);
}
if ((ret = vps_save_config(veid, dst, cmd_p, vps_p, action)))
goto err_names;
if ((ret = run_pre_script(veid, USER_CREATE_SCRIPT)))
{
logger(0, 0, "User create script " USER_CREATE_SCRIPT
" exited with error");
goto err_names;
}
logger(0, 0, "Container private area was created");
return 0;
err_names:
remove_names(veid);
err_root:
rmdir(fs->root);
err_private:
vps_destroy_dir(veid, fs->private);
err_cfg:
if (sample_config != NULL)
unlink(dst);
err:
logger(-1, 0, "Creation of container private area failed");
return ret;
}
static int vps_postcreate(envid_t veid, vps_res *res)
{
char buf[STR_SIZE];
dist_actions actions;
char *dist_name;
char *arg[2];
char *env[3];
int ret;
if (check_var(res->fs.root, "VE_ROOT is not set"))
return VZ_VE_ROOT_NOTSET;
dist_name = get_dist_name(&res->tmpl);
ret = read_dist_actions(dist_name, DIST_DIR, &actions);
free(dist_name);
if (ret)
return ret;
if (actions.post_create == NULL) {
ret = 0;
goto err;
}
ret = fsmount(veid, &res->fs, &res->dq);
if (ret)
goto err;
arg[0] = actions.post_create;
arg[1] = NULL;
snprintf(buf, sizeof(buf), "VE_ROOT=%s", res->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, res->fs.root);
err:
free_dist_actions(&actions);
return ret;
}