| /* |
| * 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 <stdio.h> |
| #include <string.h> |
| #include <getopt.h> |
| #include <signal.h> |
| #include <limits.h> |
| |
| #include "version.h" |
| #include "vzctl.h" |
| #include "env.h" |
| #include "logger.h" |
| #include "exec.h" |
| #include "vzconfig.h" |
| #include "vzerror.h" |
| #include "types.h" |
| #include "util.h" |
| #include "modules.h" |
| |
| struct mod_action g_action; |
| char *_proc_title; |
| int _proc_title_len; |
| |
| void init_modules(struct mod_action *action, const char *name); |
| void free_modules(struct mod_action *action); |
| int parse_action_opt(envid_t veid, act_t action, int *argc, char *argv[], |
| vps_param *param, const char *name); |
| 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); |
| |
| static void version(FILE *fp) |
| { |
| fprintf(fp, "vzctl version " VERSION "\n"); |
| } |
| |
| static void usage(int rc) |
| { |
| struct mod_action mod; |
| FILE *fp = rc ? stderr : stdout; |
| |
| version(fp); |
| fprintf(fp, |
| "Copyright (C) 2000-2013, Parallels, Inc.\n" |
| "This program may be distributed under the terms of the GNU GPL License.\n" |
| "\n" |
| "Usage: vzctl [<global-options>] <command> <ctid> [<options>]\n" |
| "\n" |
| "vzctl create <ctid> [--ostemplate <name>|none] [--config <name>]\n" |
| " [--layout ploop|simfs|premounted] [--hostname <name>] [--name <name>]\n" |
| " [--ipadd <addr>] [--diskspace <kibibytes>] [--diskinodes <NUM>]\n" |
| " [--private <path>] [--root <path>] [--local_uid <UID>] [--local_gid <GID>]\n" |
| "vzctl start <ctid> [--force] [--wait] [--skip-fsck] [--skip-remount]\n" |
| "vzctl destroy | mount | unmount | umount | stop | restart | status <ctid>\n" |
| #ifdef HAVE_PLOOP |
| "vzctl convert <ctid> [--layout ploop[:<mode>]]\n" |
| "vzctl compact <ctid>\n" |
| "vzctl snapshot <ctid> [--id <uuid>] [--name <name>] [--description <desc>]\n" |
| " [--skip-suspend]\n" |
| "vzctl snapshot-switch <ctid> --id <uuid> [--skip-resume | --must-resume]\n" |
| " [--skip-config]\n" |
| "vzctl snapshot-delete <ctid> --id <uuid>\n" |
| "vzctl snapshot-mount <ctid> --id <uuid> --target <dir>\n" |
| "vzctl snapshot-unmount|snapshot-umount <ctid> --id <uuid>\n" |
| "vzctl snapshot-list <ctid> [-H] [-o <field>[,<field>...]] [--id <uuid>]\n" |
| #endif |
| "vzctl quotaon | quotaoff | quotainit <ctid>\n" |
| "vzctl console <ctid> [<ttyno>]\n" |
| "vzctl enter <ctid> [--start] [--exec <command> [<arg> ...]]\n" |
| "vzctl exec | exec2 <ctid> <command> [<arg> ...]\n" |
| "vzctl runscript <ctid> <script>\n" |
| "vzctl suspend | resume <ctid> [--dumpfile <name>]\n" |
| "vzctl set <ctid> [--save] [--force] [--setmode restart|ignore]\n" |
| " [--ram <bytes>] [--swap <bytes>]\n" |
| " [--ipadd <addr>] [--ipdel <addr>|all] [--hostname <name>]\n" |
| " [--nameserver <addr>] [--searchdomain <name>]\n" |
| " [--onboot yes|no] [--bootorder <N>]\n" |
| " [--userpasswd <user>:<passwd>]\n" |
| " [--cpuunits <N>] [--cpulimit <N>] [--cpus <N>]\n" |
| " [--cpumask <cpus>] [--nodemask <nodes>]\n" |
| " [--diskspace <soft>[:<hard>]] [--diskinodes <soft>[:<hard>]]\n" |
| " [--quotatime <N>] [--quotaugidlimit <N>] [--mount_opts <opt>[,<opt>...]]\n" |
| " [--offline-resize] [--capability <name>:on|off ...]\n" |
| " [--devices b|c:<major>:<minor>|all:r|w|rw]\n" |
| " [--devnodes <device>:r|w|rw|none]\n" |
| " [--netif_add <ifname>[,<macaddr>,<host_ifname>,<host_macaddr>,<bridge>]]\n" |
| " [--netif_del <ifname>]\n" |
| " [--applyconfig <name>] [--applyconfig_map <name>]\n" |
| " [--features <name>:{on|off}[,...]] [--name <vename>]\n" |
| " [--ioprio <N>] [--iolimit <N>] [--iopslimit <N>]\n" |
| " [--pci_add [<domain>:]<bus>:<slot>.<func>] [--pci_del <d:b:s.f>]\n" |
| " [--iptables <name>] [--disabled {yes|no}]\n" |
| " [--stop-timeout <seconds>] [--init-path <path>]\n" |
| " [<UBC parameters>]\n" |
| "\n" |
| "<bytes> may be followed by a suffix to indicate multiplier, as followings:\n" |
| "B=1, K=KiB=1024, M=MiB=1024*1024, G=GiB=1024*1024*1024,\n" |
| "T=TiB=1024*1024*1024*1024, P=<bytes-per-page>\n" |
| "UBC parameters (N - items, P - pages, B - bytes):\n" |
| "Two numbers divided by colon means barrier:limit.\n" |
| "In case the limit is not given it is set to the same value as the barrier.\n" |
| " --numproc N[:N] --numtcpsock N[:N] --numothersock N[:N]\n" |
| " --vmguarpages P[:P] --kmemsize B[:B] --tcpsndbuf B[:B]\n" |
| " --tcprcvbuf B[:B] --othersockbuf B[:B] --dgramrcvbuf B[:B]\n" |
| " --oomguarpages P[:P] --lockedpages P[:P] --privvmpages P[:P]\n" |
| " --shmpages P[:P] --numfile N[:N] --numflock N[:N]\n" |
| " --numpty N[:N] --numsiginfo N[:N] --dcachesize N[:N]\n" |
| " --numiptent N[:N] --physpages P[:P] --avnumproc N[:N]\n" |
| " --swappages P[:P]\n" |
| ); |
| memset(&mod, 0, sizeof(mod)); |
| set_log_level(0); |
| init_modules(&mod, NULL); |
| mod_print_usage(&mod); |
| free_modules(&mod); |
| exit(rc); |
| } |
| |
| /* Do various checks on global config, |
| * issue warnings and/or fix things. |
| */ |
| void validate_global_config(vps_param *gparam) { |
| if (gparam->res.env.nf_mask) { |
| logger(0, 0, "Warning: NETFILTER should not be set in " |
| GLOBAL_CFG ", ignoring"); |
| logger(0, 0, "Please remove NETFILTER from " GLOBAL_CFG |
| "to get rid of this warning"); |
| gparam->res.env.nf_mask = 0; |
| } |
| #if 0 /* TODO: enable past vzctl 4.7 */ |
| if (gparam->res.env.ipt_mask) { |
| /* TODO: ignore global iptables in vzctl 4.8? */ |
| logger(0, 0, "Warning: please don't use IPTABLES in " |
| GLOBAL_CFG ", it is obsoleted by " |
| "per-CT netfilter option and will be " |
| "ignored in a future version"); |
| } |
| #endif |
| } |
| |
| int main(int argc, char *argv[], char *envp[]) |
| { |
| act_t action = -1; |
| int verbose = 0; |
| int verbose_custom = 0; |
| int quiet = 0; |
| int veid, ret, skiplock = 0; |
| char buf[256]; |
| vps_param *gparam = NULL, *vps_p = NULL, *cmd_p = NULL; |
| const char *action_nm; |
| struct sigaction act; |
| char *name = NULL, *opt; |
| |
| _proc_title = argv[0]; |
| _proc_title_len = envp[0] - argv[0]; |
| |
| gparam = init_vps_param(); |
| vps_p = init_vps_param(); |
| cmd_p = init_vps_param(); |
| |
| sigemptyset(&act.sa_mask); |
| act.sa_handler = SIG_IGN; |
| act.sa_flags = 0; |
| sigaction(SIGPIPE, &act, NULL); |
| |
| while (argc > 1) { |
| opt = argv[1]; |
| |
| if (!strcmp(opt, "--verbose")) { |
| verbose++; |
| verbose_custom = 1; |
| } else if (!strcmp(opt, "--quiet")) |
| quiet = 1; |
| else if (!strcmp(opt, "--version")) { |
| version(stdout); |
| exit(0); |
| } else if (!strcmp(opt, "--skiplock")) |
| skiplock = YES; |
| else |
| break; |
| argc--; argv++; |
| } |
| if (argc <= 1) |
| usage(VZ_INVALID_PARAMETER_SYNTAX); |
| action_nm = argv[1]; |
| init_log(NULL, 0, 1, verbose, quiet, NULL); |
| if (!strcmp(argv[1], "set")) { |
| init_modules(&g_action, "set"); |
| action = ACTION_SET; |
| } else if (!strcmp(argv[1], "create")) { |
| init_modules(&g_action, "create"); |
| action = ACTION_CREATE; |
| } else if (!strcmp(argv[1], "start")) { |
| init_modules(&g_action, "set"); |
| action = ACTION_START; |
| } else if (!strcmp(argv[1], "stop")) { |
| init_modules(&g_action, "set"); |
| action = ACTION_STOP; |
| } else if (!strcmp(argv[1], "restart")) { |
| action = ACTION_RESTART; |
| } else if (!strcmp(argv[1], "destroy") || !strcmp(argv[1], "delete")) { |
| action = ACTION_DESTROY; |
| } else if (!strcmp(argv[1], "mount")) { |
| action = ACTION_MOUNT; |
| } else if (strcmp(argv[1], "unmount") == 0 || strcmp(argv[1], "umount") == 0) { |
| action = ACTION_UNMOUNT; |
| } else if (!strcmp(argv[1], "exec3")) { |
| action = ACTION_EXEC3; |
| } else if (!strcmp(argv[1], "exec2")) { |
| action = ACTION_EXEC2; |
| } else if (!strcmp(argv[1], "exec")) { |
| action = ACTION_EXEC; |
| } else if (!strcmp(argv[1], "runscript")) { |
| action = ACTION_RUNSCRIPT; |
| } else if (!strcmp(argv[1], "enter")) { |
| action = ACTION_ENTER; |
| } else if (!strcmp(argv[1], "console")) { |
| action = ACTION_CONSOLE; |
| #ifdef HAVE_PLOOP |
| } else if (!strcmp(argv[1], "convert")) { |
| action = ACTION_CONVERT; |
| } else if (!strcmp(argv[1], "compact")) { |
| action = ACTION_COMPACT; |
| #endif |
| } else if (!strcmp(argv[1], "status")) { |
| action = ACTION_STATUS; |
| verbose = -1; |
| verbose_custom = 1; |
| set_log_verbose(verbose); /* only stderr messages */ |
| } else if (!strcmp(argv[1], "suspend") || !strcmp(argv[1], "chkpnt")) { |
| action = ACTION_SUSPEND; |
| } else if (!strcmp(argv[1], "resume") || !strcmp(argv[1], "restore")) { |
| action = ACTION_RESUME; |
| } else if (!strcmp(argv[1], "quotaon")) { |
| action = ACTION_QUOTAON; |
| } else if (!strcmp(argv[1], "quotaoff")) { |
| action = ACTION_QUOTAOFF; |
| } else if (!strcmp(argv[1], "quotainit")) { |
| action = ACTION_QUOTAINIT; |
| #ifdef HAVE_PLOOP |
| } else if (!strcmp(argv[1], "snapshot")) { |
| action = ACTION_SNAPSHOT_CREATE; |
| } else if (!strcmp(argv[1], "snapshot-switch")) { |
| action = ACTION_SNAPSHOT_SWITCH; |
| } else if (!strcmp(argv[1], "snapshot-delete")) { |
| action = ACTION_SNAPSHOT_DELETE; |
| } else if (!strcmp(argv[1], "snapshot-list")) { |
| action = ACTION_SNAPSHOT_LIST; |
| verbose = -1; |
| verbose_custom = 1; |
| set_log_verbose(verbose); /* only stderr messages */ |
| } else if (!strcmp(argv[1], "snapshot-mount")) { |
| action = ACTION_SNAPSHOT_MOUNT; |
| } else if (strcmp(argv[1], "snapshot-unmount") == 0 || strcmp(argv[1], "snapshot-umount") == 0) { |
| action = ACTION_SNAPSHOT_UNMOUNT; |
| #endif |
| } else if (!strcmp(argv[1], "--help")) { |
| usage(0); |
| } else { |
| init_modules(&g_action, action_nm); |
| action = ACTION_CUSTOM; |
| if (!g_action.mod_count) { |
| fprintf(stderr, "Bad command: %s\n", argv[1]); |
| ret = VZ_INVALID_PARAMETER_SYNTAX; |
| goto error; |
| } |
| } |
| if (argc < 3) { |
| fprintf(stderr, "CT ID missing\n"); |
| ret = VZ_INVALID_PARAMETER_VALUE; |
| goto error; |
| } |
| if (parse_int(argv[2], &veid)) { |
| name = strdup(argv[2]); |
| veid = get_veid_by_name(name); |
| } |
| if (veid < 0 || veid > VEID_MAX) { |
| fprintf(stderr, "Bad CT ID %s\n", argv[2]); |
| ret = VZ_INVALID_PARAMETER_VALUE; |
| goto error; |
| } |
| |
| argc -= 2; argv += 2; |
| /* getopt_long() prints argv[0] when reporting errors */ |
| argv[0] = _proc_title; |
| |
| /* Read global config file */ |
| if (vps_parse_config(veid, GLOBAL_CFG, gparam, &g_action)) { |
| ret = VZ_NOCONFIG; |
| goto error; |
| } |
| init_log(gparam->log.log_file, veid, gparam->log.enable != NO, |
| gparam->log.level, quiet, "vzctl"); |
| /* Set verbose level from global config if not overwriten |
| by --verbose |
| */ |
| if (!verbose_custom && gparam->log.verbose != NULL) { |
| verbose = *gparam->log.verbose; |
| verbose_custom = 1; |
| } |
| if (verbose < -1) |
| verbose = -1; |
| if (verbose_custom) |
| set_log_verbose(verbose); |
| if ((ret = parse_action_opt(veid, action, &argc, argv, cmd_p, |
| action_nm))) |
| { |
| goto error; |
| } |
| validate_global_config(gparam); |
| |
| if (veid == 0 && action != ACTION_SET) { |
| fprintf(stderr, "Only set actions are allowed for CT0\n"); |
| ret = VZ_INVALID_PARAMETER_VALUE; |
| goto error; |
| } |
| |
| get_vps_conf_path(veid, buf, sizeof(buf)); |
| if (stat_file(buf) == 1) { |
| if (vps_parse_config(veid, buf, vps_p, &g_action)) { |
| ret = VZ_NOCONFIG; |
| goto error; |
| } |
| if (name != NULL && |
| vps_p->res.name.name != NULL && |
| strcmp(name, vps_p->res.name.name)) |
| { |
| logger(-1, 0, "Unable to find container by name %s", |
| name); |
| ret = VZ_INVALID_PARAMETER_VALUE; |
| goto error; |
| } |
| /* do not use the layout from global config, autodetect it */ |
| gparam->res.fs.layout = 0; |
| } else if (action != ACTION_CREATE && |
| action != ACTION_STATUS && |
| action != ACTION_SET) |
| { |
| logger(-1, 0, "Container config file does not exist"); |
| ret = VZ_NOVECONFIG; |
| goto error; |
| } |
| merge_vps_param(gparam, vps_p); |
| merge_global_param(cmd_p, gparam); |
| ret = run_action(veid, action, gparam, vps_p, cmd_p, argc, argv, |
| skiplock); |
| |
| error: |
| free_modules(&g_action); |
| free_vps_param(gparam); |
| free_vps_param(vps_p); |
| free_vps_param(cmd_p); |
| free_log(); |
| free(name); |
| |
| return ret; |
| } |