| /* |
| * 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(¶m->res.ub) || |
| is_vswap_config(¶m->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 = ¶m->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; |
| } |