blob: 2121004d696823e9e4c7839ef9fcaa4785e6417e [file] [log] [blame] [raw]
/*
* Copyright (C) 2000-2013, 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 <sys/types.h>
#include <sys/mount.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <linux/vzcalluser.h>
#include <errno.h>
#include <signal.h>
#include <limits.h>
#include "vzctl.h"
#include "vzctl_param.h"
#include "env.h"
#include "logger.h"
#include "exec.h"
#include "vzconfig.h"
#include "vzerror.h"
#include "create.h"
#include "destroy.h"
#include "util.h"
#include "lock.h"
#include "vps_configure.h"
#include "modules.h"
#include "io.h"
#include "image.h"
#include "cpt.h"
#include "snapshot.h"
#include "cleanup.h"
extern struct mod_action g_action;
extern int do_enter(vps_handler *h, envid_t veid, const char *root,
int argc, char **argv);
extern int do_console_attach(vps_handler *h, envid_t veid, int ttyno);
static struct option set_opt[] = {
{"save", no_argument, NULL, PARAM_SAVE},
{"force", no_argument, NULL, PARAM_FORCE},
{"applyconfig", required_argument, NULL, PARAM_APPLYCONFIG},
{"applyconfig_map",
required_argument, NULL, PARAM_APPLYCONFIG_MAP},
{"reset_ub", no_argument, NULL, PARAM_RESET_UB},
{"iptables", required_argument, NULL, PARAM_IPTABLES},
{"netfilter", required_argument, NULL, PARAM_NETFILTER},
/* UB */
{"kmemsize", required_argument, NULL, PARAM_KMEMSIZE},
{"lockedpages", required_argument, NULL, PARAM_LOCKEDPAGES},
{"privvmpages", required_argument, NULL, PARAM_PRIVVMPAGES},
{"shmpages", required_argument, NULL, PARAM_SHMPAGES},
{"numproc", required_argument, NULL, PARAM_NUMPROC},
{"physpages", required_argument, NULL, PARAM_PHYSPAGES},
{"vmguarpages", required_argument, NULL, PARAM_VMGUARPAGES},
{"oomguarpages",required_argument, NULL, PARAM_OOMGUARPAGES},
{"numtcpsock", required_argument, NULL, PARAM_NUMTCPSOCK},
{"numflock", required_argument, NULL, PARAM_NUMFLOCK},
{"numpty", required_argument, NULL, PARAM_NUMPTY},
{"numsiginfo", required_argument, NULL, PARAM_NUMSIGINFO},
{"tcpsndbuf", required_argument, NULL, PARAM_TCPSNDBUF},
{"tcprcvbuf", required_argument, NULL, PARAM_TCPRCVBUF},
{"othersockbuf",required_argument, NULL, PARAM_OTHERSOCKBUF},
{"dgramrcvbuf", required_argument, NULL, PARAM_DGRAMRCVBUF},
{"numothersock",required_argument, NULL, PARAM_NUMOTHERSOCK},
{"numfile", required_argument, NULL, PARAM_NUMFILE},
{"dcachesize", required_argument, NULL, PARAM_DCACHESIZE},
{"numiptent", required_argument, NULL, PARAM_NUMIPTENT},
{"avnumproc", required_argument, NULL, PARAM_AVNUMPROC},
{"swappages", required_argument, NULL, PARAM_SWAPPAGES},
{"vm_overcommit",
required_argument, NULL, PARAM_VM_OVERCOMMIT},
/* Capability */
{"capability", required_argument, NULL, PARAM_CAP},
/* Network */
{"ipadd", required_argument, NULL, PARAM_IP_ADD},
{"ip", required_argument, NULL, PARAM_IP_ADD},
{"ipdel", required_argument, NULL, PARAM_IP_DEL},
{"skip_arpdetect",
no_argument, NULL, PARAM_SKIPARPDETECT},
{"netdev_add", required_argument, NULL, PARAM_NETDEV_ADD},
{"netdev_del", required_argument, NULL, PARAM_NETDEV_DEL},
{"hostname", required_argument, NULL, PARAM_HOSTNAME},
{"nameserver", required_argument, NULL, PARAM_NAMESERVER},
{"searchdomain",required_argument, NULL, PARAM_SEARCHDOMAIN},
{"userpasswd", required_argument, NULL, PARAM_USERPW},
/* Devices */
{"devices", required_argument, NULL, PARAM_DEVICES},
{"devnodes", required_argument, NULL, PARAM_DEVNODES},
{"pci_add", required_argument, NULL, PARAM_PCI_ADD},
{"pci_del", required_argument, NULL, PARAM_PCI_DEL},
/* fs param */
{"root", required_argument, NULL, PARAM_ROOT},
{"private", required_argument, NULL, PARAM_PRIVATE},
{"mount_opts", required_argument, NULL, PARAM_MOUNT_OPTS},
/* template */
{"ostemplate", required_argument, NULL, PARAM_OSTEMPLATE},
/* Cpu */
{"cpuunits", required_argument, NULL, PARAM_CPUUNITS},
{"cpuweight", required_argument, NULL, PARAM_CPUWEIGHT},
{"cpulimit", required_argument, NULL, PARAM_CPULIMIT},
{"cpus", required_argument, NULL, PARAM_VCPUS},
{"cpumask", required_argument, NULL, PARAM_CPUMASK},
{"nodemask", required_argument, NULL, PARAM_NODEMASK},
/* create param */
{"onboot", required_argument, NULL, PARAM_ONBOOT},
{"setmode", required_argument, NULL, PARAM_SETMODE},
{"disabled", required_argument, NULL, PARAM_DISABLED},
/* quota */
{"diskquota", required_argument, NULL, PARAM_DISK_QUOTA},
{"diskspace", required_argument, NULL, PARAM_DISKSPACE},
{"diskinodes", required_argument, NULL, PARAM_DISKINODES},
{"quotatime", required_argument, NULL, PARAM_QUOTATIME},
{"quotaugidlimit",
required_argument, NULL, PARAM_QUOTAUGIDLIMIT},
{"meminfo", required_argument, NULL, PARAM_MEMINFO},
/* netif */
{"netif_add", required_argument, NULL, PARAM_NETIF_ADD_CMD},
{"netif_del", required_argument, NULL, PARAM_NETIF_DEL_CMD},
{"mac_filter", required_argument, NULL, PARAM_NETIF_MAC_FILTER},
{"mac", required_argument, NULL, PARAM_NETIF_MAC},
{"ifname", required_argument, NULL, PARAM_NETIF_IFNAME},
{"host_mac", required_argument, NULL, PARAM_NETIF_HOST_MAC},
{"host_ifname", required_argument, NULL, PARAM_NETIF_HOST_IFNAME},
{"bridge", required_argument, NULL, PARAM_NETIF_BRIDGE},
/* name */
{"name", required_argument, NULL, PARAM_NAME},
{"features", required_argument, NULL, PARAM_FEATURES},
{"ioprio", required_argument, NULL, PARAM_IOPRIO},
{"iolimit", required_argument, NULL, PARAM_IOLIMIT_MB},
{"iopslimit", required_argument, NULL, PARAM_IOPSLIMIT},
{"description", required_argument, NULL, PARAM_DESCRIPTION},
{"bootorder", required_argument, NULL, PARAM_BOOTORDER},
/* New "easy" VSwap parameters */
{"ram", required_argument, NULL, PARAM_RAM},
{"swap", required_argument, NULL, PARAM_SWAP},
{"stop-timeout", required_argument, NULL, PARAM_STOP_TIMEOUT},
{"offline-resize", no_argument, NULL, PARAM_OFFLINE_RESIZE},
/* Alias for PVC compatibility */
{"offline", no_argument, NULL, PARAM_OFFLINE_RESIZE},
{NULL, 0, NULL, 0}
};
#define CHECK_EXTRA_ARGV \
if (optind < argc) { \
fprintf(stderr, "Invalid extra arguments: "); \
while (optind < argc) \
fprintf(stderr, "%s ", argv[optind++]); \
fprintf(stderr, "\n"); \
return VZ_INVALID_PARAMETER_SYNTAX; \
}
static int parse_opt(envid_t veid, int argc, char *argv[], struct option *opt,
struct option *internal_opt, vps_param *param)
{
int c, ret;
while (1) {
int option_index = -1;
c = getopt_long(argc, argv, PARAM_LINE, opt, &option_index);
if (c == -1)
break;
if (c == '?')
return VZ_INVALID_PARAMETER_VALUE;
ret = vps_parse_opt(veid, internal_opt, param, c, optarg, &g_action);
switch (ret) {
case 0:
case ERR_DUP:
continue;
case ERR_INVAL:
case ERR_INVAL_SKIP:
case ERR_LONG_TRUNC:
if (option_index < 0) {
logger(-1, 0, "Bad parameter for"
" -%c : %s", c, optarg);
return VZ_INVALID_PARAMETER_VALUE;
} else {
logger(-1, 0, "Bad parameter for"
" --%s: %s",
opt[option_index].name,
optarg);
return VZ_INVALID_PARAMETER_VALUE;
}
case ERR_UNK:
/* This shouldn't happen -- getopt_long()
* should have catched this already
*/
if (option_index < 0)
logger(-1, 0, "Invalid option -%c", c);
else
logger(-1, 0, "Invalid option --%s",
opt[option_index].name);
return VZ_INVALID_PARAMETER_SYNTAX;
case ERR_NOMEM:
logger(-1, 0, "Not enough memory");
return VZ_RESOURCE_ERROR;
case ERR_OTHER:
logger(-1, 0, "Error parsing options");
return VZ_SYSTEM_ERROR;
}
}
CHECK_EXTRA_ARGV
return 0;
}
static int parse_startstop_opt(int argc, char **argv, vps_param *param,
int start, int stop)
{
int c, ret = 0, idx;
struct option start_options[] = {
{"force", no_argument, NULL, PARAM_FORCE},
{"skip_ve_setup", no_argument, NULL, PARAM_SKIP_VE_SETUP},
{"wait", no_argument, NULL, PARAM_WAIT},
{"fast", no_argument, NULL, PARAM_FAST},
{"skip-umount", no_argument, NULL, PARAM_SKIP_UMOUNT},
{"skip-fsck", no_argument, NULL, PARAM_SKIP_FSCK},
{"skip-remount", no_argument, NULL, PARAM_SKIP_REMOUNT},
{ NULL, 0, NULL, 0 }
};
while (1) {
c = getopt_long (argc, argv, "", start_options, &idx);
if (c == -1)
break;
switch (c) {
case PARAM_FORCE:
if (start)
param->opt.start_force = YES;
else
goto err;
break;
case PARAM_SKIP_VE_SETUP:
if (start)
param->opt.skip_setup = YES;
else
goto err;
break;
case PARAM_WAIT:
if (start)
param->res.misc.wait = YES;
else
goto err;
break;
case PARAM_FAST:
if (stop)
param->opt.fast_kill = YES;
else
goto err;
break;
case PARAM_SKIP_UMOUNT:
if (stop)
param->opt.skip_umount = YES;
else
goto err;
break;
case PARAM_SKIP_FSCK:
if (start)
param->opt.skip_fsck = YES;
else
goto err;
break;
case PARAM_SKIP_REMOUNT:
if (start)
param->opt.skip_remount = YES;
else
goto err;
break;
default:
ret = VZ_INVALID_PARAMETER_SYNTAX;
break;
}
}
CHECK_EXTRA_ARGV
return ret;
err:
fprintf(stderr, "Option --%s is not applicable to %s command\n",
start_options[idx].name,
start ? "start" : "stop");
return VZ_INVALID_PARAMETER_SYNTAX;
}
/* Try to restore container from suspend if its default dump file exists.
* Otherwise return -1.
*/
static int try_restore(vps_handler *h, envid_t veid, vps_param *g_p,
vps_param *cmd_p, skipFlags skip)
{
char buf[STR_SIZE];
char *dumpdir = g_p->res.cpt.dumpdir;
/* if dump file exists */
get_dump_file(veid, dumpdir, buf, sizeof(buf));
if (stat_file(buf) != 1)
return -1;
/* Do restore */
logger(0, 0, "Dump file %s exists, trying to restore from it", buf);
cmd_p->res.cpt.dumpdir = dumpdir;
return vps_restore(h, veid, g_p, CMD_RESTORE, &cmd_p->res.cpt,
skip | SKIP_UMOUNT);
}
static int start(vps_handler *h, envid_t veid, vps_param *g_p,
vps_param *cmd_p)
{
int ret;
const char *private = g_p->res.fs.private;
skipFlags skip = SKIP_NONE;
if (g_p->opt.start_disabled == YES &&
cmd_p->opt.start_force != YES)
{
logger(-1, 0, "Container start disabled");
return VZ_VE_START_DISABLED;
}
if (check_var(private, "VE_PRIVATE is not set"))
return VZ_VE_PRIVATE_NOTSET;
if (stat_file(private) != 1) {
logger(-1, 0, "Container private area %s does not exist",
private);
return VZ_FS_NOPRVT;
}
if (vps_is_run(h, veid)) {
logger(-1, 0, "Container is already running");
return VZ_VE_RUNNING;
}
/* Set skip flags */
if (cmd_p->opt.skip_setup == YES)
skip |= SKIP_SETUP;
if (cmd_p->opt.skip_fsck == YES)
skip |= SKIP_FSCK;
if (cmd_p->opt.skip_remount == YES)
skip |= SKIP_REMOUNT;
/* Try restore first */
ret = try_restore(h, veid, g_p, cmd_p, skip);
if (ret == 0)
return ret;
if (ret != -1) /* We tried to restore */
skip |= SKIP_REMOUNT; /* Do not remount just mounted fs */
g_p->res.misc.wait = cmd_p->res.misc.wait;
ret = vps_start(h, veid, g_p, skip, &g_action);
return ret;
}
static int stop(vps_handler *h, envid_t veid, vps_param *g_p, vps_param *cmd_p)
{
int ret;
int stop_mode;
skipFlags skip = SKIP_NONE;
if (cmd_p->opt.fast_kill == YES)
stop_mode = M_KILL;
else
stop_mode = M_HALT;
if (cmd_p->opt.skip_umount == YES)
skip += SKIP_UMOUNT;
ret = vps_stop(h, veid, g_p, stop_mode, skip, &g_action);
return ret;
}
static int restart(vps_handler *h, envid_t veid, vps_param *g_p,
vps_param *cmd_p)
{
int ret;
logger(0, 0, "Restarting container");
if (vps_is_run(h, veid)) {
ret = stop(h, veid, g_p, cmd_p);
if (ret != 0)
return ret;
}
return start(h, veid, g_p, cmd_p);
}
static int parse_create_opt(envid_t veid, int argc, char **argv,
vps_param *param)
{
int ret;
struct option *opt;
struct option create_options[] = {
{"ostemplate", required_argument, NULL, PARAM_OSTEMPLATE},
{"config", required_argument, NULL, PARAM_CONFIG},
{"private", required_argument, NULL, PARAM_PRIVATE},
{"root", required_argument, NULL, PARAM_ROOT},
{"ipadd", required_argument, NULL, PARAM_IP_ADD},
{"hostname", required_argument, NULL, PARAM_HOSTNAME},
{"name", required_argument, NULL, PARAM_NAME},
{"description", required_argument, NULL, PARAM_DESCRIPTION},
{"layout", required_argument, NULL, PARAM_VE_LAYOUT},
{"ve_layout", required_argument, NULL, PARAM_VE_LAYOUT},
{"velayout", required_argument, NULL, PARAM_VE_LAYOUT},
{"diskspace", required_argument, NULL, PARAM_DISKSPACE},
{"diskinodes", required_argument, NULL, PARAM_DISKINODES},
{"local_uid", required_argument, NULL, PARAM_LOCAL_UID},
{"local_gid", required_argument, NULL, PARAM_LOCAL_GID},
{ NULL, 0, NULL, 0 }
};
opt = mod_make_opt(create_options, &g_action, NULL);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, create_options, param);
free(opt);
return ret;
}
static int create(vps_handler *h, envid_t veid, vps_param *vps_p,
vps_param *cmd_p)
{
int ret;
ret = vps_create(h, veid, vps_p, cmd_p, &g_action);
return ret;
}
static int destroy(vps_handler *h, envid_t veid, vps_param *g_p,
vps_param *cmd_p)
{
int ret;
ret = vps_destroy(h, veid, &g_p->res.fs, &g_p->res.cpt);
if (!ret)
remove_names(veid);
return ret;
}
#ifdef HAVE_PLOOP
static int parse_convert_opt(envid_t veid, int argc, char **argv,
vps_param *param)
{
int ret;
struct option *opt;
struct option convert_options[] = {
{"layout", required_argument, NULL, PARAM_VE_LAYOUT},
{"ve_layout", required_argument, NULL, PARAM_VE_LAYOUT},
{"velayout", required_argument, NULL, PARAM_VE_LAYOUT},
{ NULL, 0, NULL, 0 }
};
opt = mod_make_opt(convert_options, &g_action, NULL);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, convert_options, param);
free(opt);
return ret;
}
static int convert(vps_handler *h, envid_t veid, vps_param *g_p,
vps_param *cmd_p)
{
int layout = cmd_p->res.fs.layout;
int mode = cmd_p->res.fs.mode;
/* Set defaults if not specified */
if (layout == 0)
layout = VE_LAYOUT_PLOOP;
if (mode < 0)
mode = PLOOP_EXPANDED_MODE;
/* We only support ploop here */
if (layout != VE_LAYOUT_PLOOP) {
logger(-1, 0, "Only conversion to ploop is supported");
return VZ_INVALID_PARAMETER_VALUE;
}
return vzctl_env_convert_ploop(h, veid,
&g_p->res.fs, &g_p->res.dq, mode);
}
static int compact(vps_handler *h, envid_t veid, vps_param *g_p,
vps_param *cmd_p)
{
char *cmd;
int mounted;
int ret;
const char *private = g_p->res.fs.private;
const char *root = g_p->res.fs.root;
char *path = ENV_PATH;
char fname[PATH_MAX];
if (check_var(root, "VE_ROOT is not set"))
return VZ_VE_ROOT_NOTSET;
if (check_var(private, "VE_PRIVATE is not set"))
return VZ_VE_PRIVATE_NOTSET;
if (stat_file(private) != 1) {
logger(-1, 0, "Container private area %s does not exist",
private);
return VZ_FS_NOPRVT;
}
if (!ve_private_is_ploop(private)) {
logger(0, 0, "Compact only makes sense for ploop containers");
return 0;
}
mounted = vps_is_mounted(root, private);
if (mounted == 0)
{
ret = vps_mount(h, veid, &g_p->res.fs, &g_p->res.dq,
SKIP_ACTION_SCRIPT);
if (ret != 0)
return ret;
}
GET_DISK_DESCRIPTOR(fname, private);
if (asprintf(&cmd, "ploop balloon discard %s", fname) < 0) {
logger(-1, ENOMEM, "Can't allocate string for cmd");
return VZ_RESOURCE_ERROR;
}
logger(1, 0, "Executing %s", cmd);
putenv(path);
ret = system(cmd);
free(cmd);
if (mounted == 0)
vps_umount(h, veid, &g_p->res.fs, SKIP_ACTION_SCRIPT);
return (ret == 0) ? 0 : VZCTL_E_COMPACT_IMAGE;
}
#endif /* HAVE_PLOOP */
static int parse_chkpnt_opt(int argc, char **argv, vps_param *vps_p)
{
int c;
int option_index;
cpt_param *cpt = &vps_p->res.cpt;
static struct option chkpnt_options[] = {
/* sub commands */
{"dump", no_argument, NULL, PARAM_DUMP},
{"suspend", no_argument, NULL, PARAM_SUSPEND},
{"resume", no_argument, NULL, PARAM_RESUME},
{"kill", no_argument, NULL, PARAM_KILL},
{"skip_arpdetect", no_argument, NULL, PARAM_SKIPARPDETECT},
/* flags */
{"flags", required_argument, NULL, PARAM_CPU_FLAGS},
{"context", required_argument, NULL, PARAM_CPTCONTEXT},
{"dumpfile", required_argument, NULL, PARAM_DUMPFILE},
{ NULL, 0, NULL, 0 }
};
while (1) {
option_index = -1;
c = getopt_long (argc, argv, "", chkpnt_options, &option_index);
if (c == -1)
break;
switch (c) {
case PARAM_DUMPFILE:
cpt->dumpfile = strdup(optarg);
break;
case PARAM_CPTCONTEXT:
cpt->ctx = strtoul(optarg, NULL, 16);
break;
case PARAM_CPU_FLAGS:
cpt->cpu_flags = strtoul(optarg, NULL, 0);
break;
case PARAM_DUMP:
if (cpt->cmd)
goto err_syntax;
cpt->cmd = CMD_DUMP;
break;
case PARAM_KILL:
if (cpt->cmd)
goto err_syntax;
cpt->cmd = CMD_KILL;
break;
case PARAM_RESUME:
if (cpt->cmd)
goto err_syntax;
cpt->cmd = CMD_RESUME;
break;
case PARAM_SUSPEND:
if (cpt->cmd)
goto err_syntax;
cpt->cmd = CMD_SUSPEND;
break;
case PARAM_SKIPARPDETECT:
vps_p->res.net.skip_arpdetect = YES;
break;
default:
/* Error is printed by getopt_long() */
return VZ_INVALID_PARAMETER_SYNTAX;
}
}
CHECK_EXTRA_ARGV
/* Do full checkpointing */
if (!cpt->cmd)
cpt->cmd = CMD_CHKPNT;
return 0;
err_syntax:
logger(-1, 0, "Invalid syntax: only one sub command may be used");
return VZ_INVALID_PARAMETER_SYNTAX;
}
static int parse_restore_opt(int argc, char **argv, vps_param *vps_p)
{
int c;
int option_index;
cpt_param *cpt = &vps_p->res.cpt;
static struct option restore_options[] = {
/* sub commands */
{"undump", no_argument, NULL, PARAM_UNDUMP},
{"kill", no_argument, NULL, PARAM_KILL},
{"resume", no_argument, NULL, PARAM_RESUME},
/* flags */
{"dumpfile", required_argument, NULL, PARAM_DUMPFILE},
{"flags", required_argument, NULL, PARAM_CPU_FLAGS},
{"context", required_argument, NULL, PARAM_CPTCONTEXT},
{"skip_arpdetect", no_argument, NULL, PARAM_SKIPARPDETECT},
{"skip-remount", no_argument, NULL, PARAM_SKIP_REMOUNT},
{"skip-fsck", no_argument, NULL, PARAM_SKIP_FSCK},
{ NULL, 0, NULL, 0 }
};
while (1) {
option_index = -1;
c = getopt_long (argc, argv, "", restore_options, &option_index);
if (c == -1)
break;
switch (c) {
case PARAM_DUMPFILE:
cpt->dumpfile = strdup(optarg);
break;
case PARAM_CPTCONTEXT:
cpt->ctx = strtoul(optarg, NULL, 16);
break;
case PARAM_CPU_FLAGS:
cpt->cpu_flags = strtoul(optarg, NULL, 0);
break;
case PARAM_UNDUMP:
if (cpt->cmd)
goto err_syntax;
cpt->cmd = CMD_UNDUMP;
break;
case PARAM_KILL:
if (cpt->cmd)
goto err_syntax;
cpt->cmd = CMD_KILL;
break;
case PARAM_RESUME:
if (cpt->cmd)
goto err_syntax;
cpt->cmd = CMD_RESUME;
break;
case PARAM_SKIPARPDETECT:
vps_p->res.net.skip_arpdetect = YES;
break;
case PARAM_SKIP_REMOUNT:
vps_p->opt.skip_remount = YES;
break;
case PARAM_SKIP_FSCK:
vps_p->opt.skip_fsck = YES;
break;
default:
/* Error is printed by getopt_long() */
return VZ_INVALID_PARAMETER_SYNTAX;
}
}
CHECK_EXTRA_ARGV
/* Do full restore */
if (!cpt->cmd)
cpt->cmd = CMD_RESTORE;
return 0;
err_syntax:
logger(-1, 0, "Invalid syntax: only one sub command may be used");
return VZ_INVALID_PARAMETER_SYNTAX;
}
#ifdef HAVE_PLOOP
static int parse_snapshot_create_opt(envid_t veid, int argc, char **argv,
vps_param *param)
{
int ret;
struct option *opt;
static struct option snapshot_create_options[] =
{
{"id", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{"uuid", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{"name", required_argument, NULL, PARAM_SNAPSHOT_NAME},
{"description", required_argument, NULL, PARAM_SNAPSHOT_DESC},
{"skip-suspend", no_argument, NULL, PARAM_SNAPSHOT_SKIP_SUSPEND},
{"skip-config", no_argument, NULL, PARAM_SNAPSHOT_SKIP_CONFIG},
{ NULL, 0, NULL, 0 }
};
opt = mod_make_opt(snapshot_create_options, &g_action, NULL);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, snapshot_create_options, param);
free(opt);
return ret;
}
static int parse_snapshot_delete_opt(envid_t veid, int argc, char **argv,
vps_param *param)
{
int ret;
struct option *opt;
static struct option snapshot_delete_options[] =
{
{"id", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{"uuid", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{ NULL, 0, NULL, 0 }
};
opt = mod_make_opt(snapshot_delete_options, &g_action, NULL);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, snapshot_delete_options, param);
free(opt);
if (ret == 0 && param->snap.guid == NULL)
return vzctl_err(VZ_INVALID_PARAMETER_SYNTAX, 0,
"Invalid syntax: snapshot id is missing");
return ret;
}
static int parse_snapshot_switch_opt(envid_t veid, int argc, char **argv,
vps_param *param)
{
int ret;
struct option *opt;
static struct option snapshot_switch_options[] =
{
{"id", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{"uuid", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{"skip_arpdetect", no_argument, NULL, PARAM_SKIPARPDETECT},
{"skip-resume", no_argument, NULL, PARAM_SNAPSHOT_SKIP_RESUME},
{"skip-config", no_argument, NULL, PARAM_SNAPSHOT_SKIP_CONFIG},
{"must-resume", no_argument, NULL, PARAM_SNAPSHOT_MUST_RESUME},
{ NULL, 0, NULL, 0 }
};
opt = mod_make_opt(snapshot_switch_options, &g_action, NULL);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, snapshot_switch_options, param);
free(opt);
if (ret == 0) {
if (param->snap.guid == NULL)
return vzctl_err(VZ_INVALID_PARAMETER_SYNTAX, 0,
"Invalid syntax: snapshot id is missing");
if ((param->snap.flags & SNAPSHOT_SKIP_RESUME) &&
(param->snap.flags & SNAPSHOT_MUST_RESUME))
return vzctl_err(VZ_INVALID_PARAMETER_SYNTAX, 0,
"Invalid syntax: --skip-resume and "
"--must-resume can not be used together");
}
return ret;
}
static int parse_snapshot_mount_opt(envid_t veid, int argc, char **argv,
vps_param *param)
{
int ret;
struct option *opt;
static struct option snapshot_create_options[] =
{
{"id", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{"uuid", required_argument, NULL, PARAM_SNAPSHOT_GUID},
{"target", required_argument, NULL, PARAM_SNAPSHOT_MOUNT_TARGET},
{ NULL, 0, NULL, 0 }
};
opt = mod_make_opt(snapshot_create_options, &g_action, NULL);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, snapshot_create_options, param);
free(opt);
if (ret == 0 && param->snap.guid == NULL)
return vzctl_err(VZ_INVALID_PARAMETER_SYNTAX, 0,
"Invalid syntax: snapshot id is missing");
if (ret == 0 && param->snap.target == NULL)
return vzctl_err(VZ_INVALID_PARAMETER_SYNTAX, 0,
"Invalid syntax: mount target is missing");
return ret;
}
#endif /* HAVE_PLOOP */
static int check_set_ugidlimit(unsigned long *cur, unsigned long *old,
int loud)
{
unsigned long c, o = 0;
if (!cur)
return 0;
c = *cur;
if (old)
o = *old;
if (c != 0 && o == 0) {
if (loud)
logger(-1, 0, "Unable to turn on second-level"
" disk quota on a running container");
return 1;
}
if (c == 0 && o != 0) {
if (loud)
logger(-1, 0, "Unable to turn off second-level"
" disk quota on a running container");
return 1;
}
return 0;
}
/* Check parameters that can't be set on running CT */
static int check_set_mode(vps_handler *h, envid_t veid, int setmode, int apply,
vps_res *new_res, vps_res *old_res)
{
int found = 0;
int loud = (setmode != SET_RESTART);
/* If some caps are set and they differ from the old ones */
if ( (new_res->cap.on &&
(new_res->cap.on != old_res->cap.on)) ||
(new_res->cap.off &&
(new_res->cap.off != old_res->cap.off)) ) {
if (loud)
logger(-1, 0, "Unable to set capability "
"on a running container");
found++;
}
/* If iptables mask is set and it differs from the old one.
* FIXME: we don't catch the case when the new set is empty
* and the old one is not (vzctl set --iptables '') */
if ( (new_res->env.ipt_mask &&
new_res->env.ipt_mask != old_res->env.ipt_mask) ||
(new_res->env.nf_mask &&
new_res->env.nf_mask != old_res->env.nf_mask))
{
if (loud)
logger(-1, 0, "Unable to set iptables/netfilter "
"on a running container");
found++;
}
/* If some features are set (to either on or off)
* and the new mask differs from the old one */
if (new_res->env.features_known &&
new_res->env.features_mask !=
old_res->env.features_mask)
{
if (loud)
logger(-1, 0, "Unable to set features "
"on a running container");
found++;
}
/* Turning quota ugid limit on/off */
found += check_set_ugidlimit(new_res->dq.ugidlimit,
old_res->dq.ugidlimit, loud);
/* Enabling/disabling DISK_QUOTA */
if ( (new_res->dq.enable) &&
(new_res->dq.enable != old_res->dq.enable) ) {
if (loud)
logger(-1, 0, "Unable to switch DISK_QUOTA on or off "
"on a running container");
found++;
}
/* Changing mount_opts */
if ( new_res->fs.mount_opts && (!old_res->fs.mount_opts ||
strcmp(new_res->fs.mount_opts,
old_res->fs.mount_opts))) {
if (loud)
logger(-1, 0, "Unable to change mount options "
"on a running container");
found++;
}
if (!found)
return 0;
switch (setmode) {
case SET_RESTART:
return 1;
case SET_IGNORE:
return -1;
case SET_NONE:
default:
logger(-1, 0, "WARNING: Some of the parameters could "
"not be applied to a running container.\n"
"\tPlease consider using --setmode option");
return 0;
}
}
static void merge_apply_param(vps_param *old, vps_param *new, char *cfg)
{
#define FREE_STR(x) free(x); x = NULL;
FREE_STR(new->res.fs.root_orig)
FREE_STR(new->res.fs.private_orig)
FREE_STR(new->res.tmpl.ostmpl)
FREE_STR(new->res.tmpl.dist)
FREE_STR(new->res.misc.hostname)
FREE_STR(new->res.misc.description)
#undef FREE_STR
free_str_param(&new->res.net.ip);
if (new->opt.origin_sample == NULL)
new->opt.origin_sample = strdup(cfg);
merge_vps_param(old, new);
}
static int apply_param_from_cfg(int veid, vps_param *param, char *cfg)
{
char conf[STR_SIZE];
vps_param *new;
if (cfg == NULL)
return 0;
snprintf(conf, sizeof(conf), VPSCONFDIR "/ve-%s.conf-sample", cfg);
if (stat_file(conf) != 1) {
logger(-1, 0, "Sample config file not found: %s", conf);
return VZ_APPLY_CONFIG_ERROR;
}
new = init_vps_param();
vps_parse_config(veid, conf, new, &g_action);
merge_apply_param(param, new, cfg);
free_vps_param(new);
return 0;
}
static int check_set_opt(int argc, char *argv[], vps_param *param)
{
if ((param->opt.reset_ub == YES) && (argc > 1)) {
logger(-1, 0, "Error: option --reset_ub is exclusive");
return VZ_INVALID_PARAMETER_SYNTAX;
}
/* VSwap config requires SWAPPAGES to be set */
if ((is_vswap_config(&param->res.ub) ||
is_vswap_config(&param->g_param->res.ub)) &&
(!param->res.ub.swappages &&
!param->g_param->res.ub.swappages)) {
logger(-1, 0, "Error: required UB parameter (swap) not set");
return VZ_NOTENOUGHUBCPARAMS;
}
/* --offline-resize can only be used with --diskspace on ploop */
if (param->res.dq.offline_resize == YES) {
if (!param->res.dq.diskspace) {
logger(-1, 0, "Error: option --offline-resize "
"can only be used with --diskspace");
return VZ_INVALID_PARAMETER_SYNTAX;
}
if (!ve_private_is_ploop(param->g_param->res.fs.private)) {
logger(0, 0, "Warning: option --offline-resize "
"only makes sense for a ploop CT");
}
}
/* --nodemask requires --cpumask */
if (param->res.cpu.nodemask &&
!(param->res.cpu.mask || param->res.cpu.cpumask_auto)) {
logger(-1, 0, "Error: option --nodemask requires --cpumask");
return VZ_INVALID_PARAMETER_SYNTAX;
}
/* --cpumask auto doesn't make sense without --nodemask */
if (param->res.cpu.cpumask_auto && !param->res.cpu.nodemask) {
logger(-1, 0, "Error: --cpumask auto requires --nodemask");
return VZ_INVALID_PARAMETER_SYNTAX;
}
/* A few options require --save flag */
if (!param->opt.save) {
if (param->res.name.name != NULL) {
logger(-1, 0, "Error: unable to use"
" --name option without --save");
return VZ_SET_NAME_ERROR;
}
if (param->res.tmpl.ostmpl) {
logger(-1, 0, "Error: unable to use"
" --ostemplate option without --save");
return VZ_INVALID_PARAMETER_SYNTAX;
}
if (param->res.misc.stop_timeout >= 0) {
logger(-1, 0, "Error: unable to use"
" --stop-timeout option without --save");
return VZ_INVALID_PARAMETER_SYNTAX;
}
if (param->opt.save_force) {
logger(-1, 0, "Error: --force is useless "
"without --save");
return VZ_INVALID_PARAMETER_SYNTAX;
}
if (param->res.dq.diskspace &&
ve_private_is_ploop(param->g_param->res.fs.private))
{
logger(-1, 0, "Error: can't use --diskspace without --save "
"for a ploop-based container");
return VZ_INVALID_PARAMETER_SYNTAX;
}
if (param->opt.setmode == SET_RESTART) {
logger(-1, 0, "Error: --setmode restart doesn't make sense"
" without --save flag");
return VZ_INVALID_PARAMETER_SYNTAX;
}
}
return 0;
}
static int parse_set_opt(envid_t veid, int argc, char *argv[],
vps_param *param)
{
int ret;
struct option *opt;
opt = mod_make_opt(set_opt, &g_action, NULL);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, set_opt, param);
free(opt);
return ret;
}
static int set_ve0(vps_handler *h, vps_param *g_p,
vps_param *vps_p, vps_param *cmd_p, int *warn_save)
{
int ret;
ub_param *ub;
cpu_param *cpu = NULL;
if (cmd_p->opt.reset_ub == YES) {
/* Apply parameters from config */
ub = &vps_p->res.ub;
*warn_save = 0;
} else {
/* Apply parameters from command line */
ub = &cmd_p->res.ub;
cpu = &cmd_p->res.cpu;
}
if (!h)
return 0;
ret = vps_set_ublimit(h, 0, ub);
if (ret)
return ret;
if ((ret = vps_set_io(h, 0, &cmd_p->res.io)))
return ret;
if (cpu != NULL)
if ((ret = hn_set_cpu(cpu)))
return ret;
return 0;
}
/* If we are adding an IP, and the same IP is already in config,
* add it to 'del' list as well, meaning we will remove and add it again.
* This is mostly helpful when we do --ipadd with the same IP but
* different netmask (note that find_ip() is discarding netmask).
*/
static int fix_ip_param(vps_param *conf, vps_param *cmd)
{
ip_param *ip;
char *found;
list_head_t *cur = &conf->res.net.ip;
list_head_t *add = &cmd->res.net.ip;
list_head_t *del = &cmd->del_res.net.ip;
if (list_empty(add))
return 0;
list_for_each(ip, add, list) {
found = find_ip(cur, ip->val);
if ((found) && (!find_ip(del, ip->val))) {
add_str_param(del, found);
}
}
return 0;
}
static int set(vps_handler *h, envid_t veid, vps_param *g_p, vps_param *vps_p,
vps_param *cmd_p, int argc, char **argv, int *warn_save)
{
int ret = 0, is_run;
dist_actions *actions = NULL;
fix_ip_param(g_p, cmd_p);
if (!list_empty(&cmd_p->res.veth.dev) ||
!list_empty(&cmd_p->del_res.veth.dev))
{
ret = check_veth_param(veid, &vps_p->res.veth, &cmd_p->res.veth,
&cmd_p->del_res.veth);
if (ret) {
*warn_save = 0;
return ret;
}
}
cmd_p->g_param = g_p;
ret = check_set_opt(argc, argv, cmd_p);
if (ret)
return ret;
if (cmd_p->opt.apply_cfg_map == APPCONF_MAP_NAME) {
ret = set_name(veid, g_p->res.name.name, g_p->res.name.name);
if (ret != 0)
return ret;
}
is_run = h != NULL && vps_is_run(h, veid);
/* Reset UB parameters from config */
if (cmd_p->opt.reset_ub == YES) {
if (!is_run) {
logger(-1, 0, "Container is not running");
return VZ_VE_NOT_RUNNING;
}
ret = vps_set_ublimit(h, veid, &vps_p->res.ub);
*warn_save = 0;
return ret;
}
if (cmd_p->res.name.name != NULL) {
ret = set_name(veid, cmd_p->res.name.name,
vps_p->res.name.name);
if (ret) {
*warn_save = 0;
return ret;
}
}
if (is_run) {
if (cmd_p->res.fs.private_orig != NULL) {
free(cmd_p->res.fs.private_orig);
cmd_p->res.fs.private_orig = NULL;
logger(-1, 0, "Unable to change VE_PRIVATE "
"on a running container");
return VZ_VE_RUNNING;
}
if (cmd_p->res.fs.root_orig != NULL) {
free(cmd_p->res.fs.root_orig);
cmd_p->res.fs.root_orig = NULL;
logger(-1, 0, "Unable to change VE_ROOT "
"on a running container");
return VZ_VE_RUNNING;
}
}
if (need_configure(&cmd_p->res) ||
need_configure(&cmd_p->del_res) ||
!list_empty(&cmd_p->res.misc.userpw))
{
char *dist_name;
actions = vz_malloc(sizeof(*actions));
if (!actions)
return VZ_RESOURCE_ERROR;
dist_name = get_dist_name(&g_p->res.tmpl);
ret = read_dist_actions(dist_name, DIST_DIR, actions);
free(dist_name);
if (ret) {
free(actions);
return ret;
}
}
/* Setup password */
if (h != NULL && !list_empty(&cmd_p->res.misc.userpw)) {
if (!is_run) {
ret = vps_start(h, veid, g_p,
SKIP_SETUP|SKIP_ACTION_SCRIPT, NULL);
if (ret)
goto err;
}
if (argc == 2) /* only --userpasswd and its value */
*warn_save = 0;
ret = vps_pw_configure(h, veid, actions, g_p->res.fs.root,
&cmd_p->res.misc.userpw);
if (!is_run)
vps_stop(h, veid, g_p, M_KILL, SKIP_ACTION_SCRIPT,
NULL);
if (ret)
goto err;
}
if (cmd_p->opt.apply_cfg != NULL) {
if ((ret = apply_param_from_cfg(veid, cmd_p,
cmd_p->opt.apply_cfg)))
{
goto err;
}
}
/* Resize ploop image if --diskspace is supplied */
if (cmd_p->res.dq.diskspace &&
ve_private_is_ploop(g_p->res.fs.private))
{
if (! is_ploop_supported()) {
ret=VZ_PLOOP_UNSUP;
goto err;
}
/* offline resize requires unmounted container */
if (cmd_p->res.dq.offline_resize == YES) {
int is_mounted = (vps_is_mounted(g_p->res.fs.root,
g_p->res.fs.private) == 1);
if (is_run || is_mounted) {
logger(-1, 0, "Option --offline-resize "
"can't be used on a "
"%s container",
is_run ? "running"
: "mounted");
ret = VZ_INVALID_PARAMETER_VALUE;
goto err;
}
}
#ifdef HAVE_PLOOP
if ((ret = vzctl_resize_image(g_p->res.fs.private,
cmd_p->res.dq.diskspace[1],
cmd_p->res.dq.offline_resize)))
goto err;
#endif
}
/* Warn that --diskinodes is ignored for ploop CT */
if (cmd_p->res.dq.diskinodes &&
ve_private_is_ploop(g_p->res.fs.private))
{
logger(0, 0, "Warning: --diskinodes is ignored for "
"ploop-based container");
}
/* Skip applying parameters on stopped CT */
if (cmd_p->opt.save && !is_run) {
ret = mod_setup(h, veid, STATE_STOPPED, SKIP_NONE, &g_action,
cmd_p);
goto err;
}
if (is_run) {
/* Check if restart is required */
ret = check_set_mode(h, veid, cmd_p->opt.setmode,
cmd_p->opt.save, &cmd_p->res, &g_p->res);
if (ret) {
if (ret < 0) {
/* ignore but return error */
ret = VZ_VE_RUNNING;
} else {
/* do restart */
merge_vps_param(g_p, cmd_p);
ret = restart(h, veid, g_p, cmd_p);
}
goto err;
}
}
/* Only try to apply parameters on a supported kernel */
if (h) {
ub_param calc_ub = {};
merge_ub(&calc_ub, &cmd_p->res.ub);
ret = fill_vswap_ub(&g_p->res.ub, &calc_ub);
if (!ret) {
/* If CT is not running, call this anyway
* to get relevant errors messages like
* "Can't set CPU: CT is not running"
*/
ret = vps_setup_res(h, veid, actions,
&g_p->res.fs, &calc_ub, cmd_p,
STATE_RUNNING, SKIP_NONE, &g_action);
}
free_ub_param(&calc_ub);
}
err:
free_dist_actions(actions);
free(actions);
return ret;
}
static int vps_set(vps_handler *h, envid_t veid,
vps_param *g_p, vps_param *vps_p, vps_param *cmd_p,
int argc, char **argv)
{
int ret;
int warn_save = 1;
char fname[STR_SIZE];
if (veid == 0)
ret = set_ve0(h, g_p, vps_p, cmd_p, &warn_save);
else
ret = set(h, veid, g_p, vps_p, cmd_p, argc, argv, &warn_save);
if (cmd_p->opt.save == YES) {
if (ret) {
logger(-1, 0, "Error: failed to apply "
"some parameters, not saving "
"configuration file!");
return ret;
}
get_vps_conf_path(veid, fname, sizeof(fname));
/* Warn if config does not exist */
if (stat_file(fname) != 1)
logger(-1, errno, "WARNING: %s not found", fname);
ret = vps_save_config(veid, fname, cmd_p, vps_p, &g_action);
}
else if (warn_save && !ret) {
int is_run = h != NULL && vps_is_run(h, veid);
logger(0, 0, "WARNING: Settings were not saved"
" to config %s(use --save flag)",
is_run ? "and will be lost after CT restart "
: "");
}
return ret;
}
static int enter(vps_handler *h, envid_t veid, const char *root,
int argc, char **argv)
{
if (check_var(root, "VE_ROOT is not set"))
return VZ_VE_ROOT_NOTSET;
if (!vps_is_run(h, veid)) {
logger(-1, 0, "Container is not running");
return VZ_VE_NOT_RUNNING;
}
if (argc > 0) {
/* omit "--exec" argument */
argc -= 1; argv += 1;
}
logger(1, 0, "Entering CT");
set_log_file(NULL);
return do_enter(h, veid, root, argc, argv);
}
static int console_attach(vps_handler *h, envid_t veid, int argc, char **argv)
{
int tty = -1;
if (argc > 1)
return vzctl_err(VZ_INVALID_PARAMETER_SYNTAX, 0,
"Invalid syntax: too many parameters!");
if (argc == 1) {
if (parse_int(argv[0], &tty) != 0)
goto err;
if (tty < 0)
goto err;
}
return do_console_attach(h, veid, tty);
err:
return vzctl_err(VZ_INVALID_PARAMETER_VALUE, 0,
"Invalid tty number: %s", argv[0]);
}
static int exec(vps_handler *h, act_t action, envid_t veid, const char *root,
int argc, char **argv)
{
int mode;
char **arg = NULL;
char *argv_bash[] = {"bash", "-c", NULL, NULL};
char *buf = NULL;
int i, ret;
int len, totallen = 0;
mode = MODE_BASH;
/* Unroll argv */
for (i = 0; i < argc; i++) {
len = strlen(argv[i]);
if (len) {
buf = (char*)realloc(buf, totallen + len + 2);
sprintf(buf + totallen, "%s ", argv[i]);
} else {
/* set empty argument */
len = 2;
buf = (char*)realloc(buf, totallen + len + 2);
sprintf(buf + totallen, "\'\' ");
}
totallen += len + 1;
buf[totallen] = 0;
}
if (action == ACTION_EXEC3) {
arg = argv;
mode = MODE_EXEC;
} else if (argc && strcmp(argv[0], "-")) {
argv_bash[2] = buf;
arg = argv_bash;
}
logger(1, 0, "Executing command: %s", buf);
ret = vps_exec(h, veid, root, mode, arg, NULL, NULL, 0);
free(buf);
return ret;
}
static int chkpnt(vps_handler *h, envid_t veid, vps_param *g_p, vps_param *cmd_p)
{
int cmd, ret;
cmd = cmd_p->res.cpt.cmd;
merge_vps_param(g_p, cmd_p);
cmd_p->res.cpt.dumpdir = g_p->res.cpt.dumpdir;
if (cmd == CMD_KILL || cmd == CMD_RESUME) {
/* FIXME: CRIU support? */
ret = cpt_cmd(h, veid, g_p->res.fs.root,
CMD_CHKPNT, cmd, cmd_p->res.cpt.ctx);
if (ret)
return ret;
if (cmd == CMD_RESUME) {
/* restore arp/routing cleared on dump stage */
run_net_script(veid, ADD, &g_p->res.net.ip,
STATE_RUNNING,
cmd_p->res.net.skip_arpdetect);
}
return 0;
}
ret = vps_chkpnt(h, veid, &g_p->res.fs, cmd, &cmd_p->res.cpt);
if (ret)
return ret;
if (cmd == CMD_CHKPNT || cmd == CMD_DUMP) {
/* Clear CT network configuration */
run_net_script(veid, DEL, &g_p->res.net.ip, STATE_RUNNING,
cmd_p->res.net.skip_arpdetect);
if (cmd == CMD_CHKPNT)
vps_umount(h, veid, &g_p->res.fs, 0);
}
return 0;
}
static int restore(vps_handler *h, envid_t veid, vps_param *g_p,
vps_param *cmd_p)
{
int cmd;
int skip = SKIP_NONE;
cmd = cmd_p->res.cpt.cmd;
merge_vps_param(g_p, cmd_p);
cmd_p->res.cpt.dumpdir = g_p->res.cpt.dumpdir;
if (cmd == CMD_KILL || cmd == CMD_RESUME)
return cpt_cmd(h, veid, g_p->res.fs.root,
CMD_RESTORE, cmd, cmd_p->res.cpt.ctx);
if (cmd_p->opt.skip_remount == YES)
skip |= SKIP_REMOUNT;
if (cmd_p->opt.skip_fsck == YES)
skip |= SKIP_FSCK;
return vps_restore(h, veid, g_p, cmd, &cmd_p->res.cpt, skip);
}
static int show_status(vps_handler *h, envid_t veid, vps_param *param)
{
int exist, mounted, run, suspended = 0;
char buf[STR_SIZE];
fs_param *fs = &param->res.fs;
get_vps_conf_path(veid, buf, sizeof(buf));
exist = (fs->private != NULL && stat_file(fs->private) == 1
&& stat_file(buf) == 1);
mounted = (vps_is_mounted(fs->root, fs->private) == 1);
run = vps_is_run(h, veid);
if (exist && !run) {
get_dump_file(veid, param->res.cpt.dumpdir, buf, sizeof(buf));
suspended = (stat_file(buf) == 1);
}
printf("CTID %d %s %s %s%s\n", veid,
exist ? "exist" : "deleted",
mounted ? "mounted" : "unmounted",
run ? "running" : "down",
suspended ? " suspended" : "");
return 0;
}
static int parse_custom_opt(envid_t veid, int argc, char **argv,
vps_param *param, const char *name)
{
int ret;
struct option *opt;
opt = mod_make_opt(NULL, &g_action, name);
if (opt == NULL)
return VZ_RESOURCE_ERROR;
ret = parse_opt(veid, argc, argv, opt, NULL, param);
free(opt);
return ret;
}
int parse_action_opt(envid_t veid, act_t action, int argc, char *argv[],
vps_param *param, const char *name)
{
int ret = 0;
switch (action) {
case ACTION_SET:
ret = parse_set_opt(veid, argc, argv, param);
break;
case ACTION_CREATE:
ret = parse_create_opt(veid, argc, argv, param);
break;
#ifdef HAVE_PLOOP
case ACTION_CONVERT:
ret = parse_convert_opt(veid, argc, argv, param);
break;
#endif
case ACTION_START:
ret = parse_startstop_opt(argc, argv, param, 1, 0);
break;
case ACTION_STOP:
ret = parse_startstop_opt(argc, argv, param, 0, 1);
break;
case ACTION_RESTART:
ret = parse_startstop_opt(argc, argv, param, 1, 1);
break;
case ACTION_ENTER:
if (argc >= 2) {
if (!strcmp(argv[1], "--exec")) {
if ((argc == 2) || (*argv[2] == '\0')) {
fprintf(stderr,
"No command line "
"given for --exec\n");
ret = VZ_INVALID_PARAMETER_SYNTAX;
}
} else {
fprintf(stderr,
"Invalid option '%s'\n", argv[1]);
ret = VZ_INVALID_PARAMETER_SYNTAX;
}
}
break;
case ACTION_EXEC:
case ACTION_EXEC2:
case ACTION_EXEC3:
if (argc < 2) {
fprintf(stderr, "No command line given for exec\n");
ret = VZ_INVALID_PARAMETER_SYNTAX;
}
break;
case ACTION_RUNSCRIPT:
if (argc < 2) {
fprintf(stderr, "Script not specified\n");
ret = VZ_INVALID_PARAMETER_SYNTAX;
}
break;
case ACTION_CUSTOM:
ret = parse_custom_opt(veid, argc, argv, param, name);
break;
case ACTION_SUSPEND:
ret = parse_chkpnt_opt(argc, argv, param);
break;
case ACTION_RESUME:
ret = parse_restore_opt(argc, argv, param);
break;
case ACTION_CONSOLE:
break;
#ifdef HAVE_PLOOP
case ACTION_SNAPSHOT_CREATE:
ret = parse_snapshot_create_opt(veid, argc, argv, param);
break;
case ACTION_SNAPSHOT_SWITCH:
ret = parse_snapshot_switch_opt(veid, argc, argv, param);
break;
case ACTION_SNAPSHOT_DELETE:
case ACTION_SNAPSHOT_UMOUNT:
ret = parse_snapshot_delete_opt(veid, argc, argv, param);
break;
case ACTION_SNAPSHOT_LIST:
break;
case ACTION_SNAPSHOT_MOUNT:
ret = parse_snapshot_mount_opt(veid, argc, argv, param);
break;
#endif
default :
if ((argc - 1) > 0) {
fprintf (stderr, "Invalid options: ");
while (--argc)
fprintf(stderr, "%s ", *(++argv));
fprintf (stderr, "\n");
ret = VZ_INVALID_PARAMETER_SYNTAX;
}
}
return ret;
}
static void cleanup_callback(int sig)
{
fprintf(stderr, "\nCancelling...\n");
run_cleanup();
}
int run_action(envid_t veid, act_t action, vps_param *g_p, vps_param *vps_p,
vps_param *cmd_p, int argc, char **argv, int skiplock)
{
vps_handler *h = NULL;
int ret = 0, lock_id = -1;
struct sigaction act;
if ((h = vz_open(veid, g_p)) == NULL) {
/* Accept to run "set --save --force" on any kernel,
* otherwise error out if initialization failed
*/
if (action != ACTION_SET ||
cmd_p->opt.save_force != YES ||
cmd_p->opt.save != YES)
{
return VZ_BAD_KERNEL;
}
}
if (!is_vz_kernel(h)) {
/* No simfs support, emulate it with a bind mount */
g_p->res.fs.flags |= MS_BIND;
/* No quotas, regardless of what the config says */
g_p->res.dq.enable = NO;
}
if (action != ACTION_EXEC &&
action != ACTION_EXEC2 &&
action != ACTION_EXEC3 &&
action != ACTION_ENTER &&
action != ACTION_CONSOLE &&
action != ACTION_STATUS)
{
if (skiplock != YES) {
lock_id = vps_lock(veid, g_p->opt.lockdir, "");
if (lock_id > 0) {
logger(-1, 0, "Container already locked");
ret = VZ_LOCKED;
goto err;
} else if (lock_id < 0) {
logger(-1, 0, "Unable to lock container");
ret = VZ_SYSTEM_ERROR;
goto err;
}
}
sigemptyset(&act.sa_mask);
act.sa_handler = cleanup_callback;
act.sa_flags = 0;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
}
switch (action) {
case ACTION_CREATE:
ret = create(h, veid, g_p, cmd_p);
break;
case ACTION_DESTROY:
ret = destroy(h, veid, g_p, cmd_p);
break;
case ACTION_MOUNT:
ret = vps_mount(h, veid, &g_p->res.fs, &g_p->res.dq, 0);
break;
case ACTION_UMOUNT:
ret = vps_umount(h, veid, &g_p->res.fs, 0);
break;
case ACTION_START:
ret = start(h, veid, g_p, cmd_p);
break;
case ACTION_STOP:
ret = stop(h, veid, g_p, cmd_p);
break;
case ACTION_RESTART:
ret = restart(h, veid, g_p, cmd_p);
break;
#ifdef HAVE_PLOOP
case ACTION_CONVERT:
ret = convert(h, veid, g_p, cmd_p);
break;
case ACTION_COMPACT:
ret = compact(h, veid, g_p, cmd_p);
break;
#endif
case ACTION_SET:
ret = vps_set(h, veid, g_p, vps_p, cmd_p, argc-1, argv+1);
break;
case ACTION_STATUS:
ret = show_status(h, veid, g_p);
break;
case ACTION_ENTER:
ret = enter(h, veid, g_p->res.fs.root, argc-1, argv+1);
break;
case ACTION_CONSOLE:
ret = console_attach(h, veid, argc-1, argv+1);
break;
case ACTION_EXEC:
case ACTION_EXEC2:
case ACTION_EXEC3:
ret = exec(h, action, veid, g_p->res.fs.root, argc-1, argv+1);
if (ret && action == ACTION_EXEC)
ret = VZ_COMMAND_EXECUTION_ERROR;
break;
case ACTION_SUSPEND:
ret = chkpnt(h, veid, g_p, cmd_p);
break;
case ACTION_RESUME:
ret = restore(h, veid, g_p, cmd_p);
break;
case ACTION_RUNSCRIPT:
ret = vps_run_script(h, veid, argv[1], g_p);
break;
#define CHECK_DQ(r) \
if (g_p->res.dq.enable != YES) { \
ret = r; \
logger(0, 0, "Disk quota is not enabled, skipping operation"); \
break; \
} \
if (ve_private_is_ploop(g_p->res.fs.private)) { \
ret = 0; \
logger(0, 0, "CT is on ploop, disk quota is obsoleted, " \
"skipping operation"); \
break; \
}
case ACTION_QUOTAON:
CHECK_DQ(VZ_DQ_ON)
ret = vps_quotaon(veid, g_p->res.fs.private, &g_p->res.dq);
break;
case ACTION_QUOTAOFF:
CHECK_DQ(VZ_DQ_OFF)
ret = vps_quotaoff(veid, &g_p->res.dq);
break;
case ACTION_QUOTAINIT:
CHECK_DQ(VZ_DQ_INIT)
ret = quota_init(veid, g_p->res.fs.private, &g_p->res.dq);
break;
#undef CHECK_DQ
#ifdef HAVE_PLOOP
case ACTION_SNAPSHOT_CREATE:
ret = vzctl_env_create_snapshot(h, veid,
&g_p->res.fs, &cmd_p->snap);
break;
case ACTION_SNAPSHOT_DELETE:
ret = vzctl_env_delete_snapshot(h, veid,
&g_p->res.fs, cmd_p->snap.guid);
break;
case ACTION_SNAPSHOT_SWITCH:
g_p->res.net.skip_arpdetect = cmd_p->res.net.skip_arpdetect;
ret = vzctl_env_switch_snapshot(h, veid, g_p, &cmd_p->snap);
break;
case ACTION_SNAPSHOT_LIST:
ret = vzctl_env_snapshot_list(argc, argv, veid,
g_p->res.fs.private);
break;
case ACTION_SNAPSHOT_MOUNT:
ret = vzctl_env_mount_snapshot(veid, g_p->res.fs.private,
cmd_p->snap.target, cmd_p->snap.guid);
break;
case ACTION_SNAPSHOT_UMOUNT:
ret = vzctl_env_umount_snapshot(veid, g_p->res.fs.private,
cmd_p->snap.guid);
break;
#endif
case ACTION_CUSTOM:
ret = mod_setup(h, veid, 0, 0, &g_action, g_p);
break;
}
err:
/* Unlock CT in case lock taken */
if (skiplock != YES && !lock_id)
vps_unlock(veid, g_p->opt.lockdir);
vz_close(h);
return ret;
}