diff mbox

[RFC/PATCH] kvm tools: Introduce 'kvm setup' command

Message ID 1314197019-10307-1-git-send-email-penberg@kernel.org (mailing list archive)
State New, archived
Headers show

Commit Message

Pekka Enberg Aug. 24, 2011, 2:43 p.m. UTC
This patch implements 'kvm setup' command that can be used to setup a guest
filesystem that shares system libraries and binaries from host filesystem in
read-only mode.

You can setup a new shared rootfs guest with:

  ./kvm setup -n default

and launch it with:

  ./kvm run --9p /,hostfs -p "init=virt/init" -d ~/.kvm-tools/default/

We want to teach 'kvm run' to be able to launch guest filesystems by name in
the future. Furthermore, 'kvm run' should setup a 'default' filesystem and use
it by default unless the user specifies otherwise.

Cc: Asias He <asias.hejun@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
---
 tools/kvm/.gitignore                  |    1 +
 tools/kvm/Documentation/kvm-setup.txt |   15 +++
 tools/kvm/Makefile                    |   11 ++-
 tools/kvm/builtin-setup.c             |  184 +++++++++++++++++++++++++++++++++
 tools/kvm/command-list.txt            |    1 +
 tools/kvm/guest/init.c                |   40 +++++++
 tools/kvm/include/kvm/builtin-setup.h |    7 ++
 tools/kvm/kvm-cmd.c                   |    2 +
 8 files changed, 259 insertions(+), 2 deletions(-)
 create mode 100644 tools/kvm/Documentation/kvm-setup.txt
 create mode 100644 tools/kvm/builtin-setup.c
 create mode 100644 tools/kvm/guest/init.c
 create mode 100644 tools/kvm/include/kvm/builtin-setup.h
diff mbox

Patch

diff --git a/tools/kvm/.gitignore b/tools/kvm/.gitignore
index 852d052..6ace4ec 100644
--- a/tools/kvm/.gitignore
+++ b/tools/kvm/.gitignore
@@ -6,4 +6,5 @@  tags
 include/common-cmds.h
 tests/boot/boot_test.iso
 tests/boot/rootfs/
+guest/init
 KVMTOOLS-VERSION-FILE
diff --git a/tools/kvm/Documentation/kvm-setup.txt b/tools/kvm/Documentation/kvm-setup.txt
new file mode 100644
index 0000000..c845d17
--- /dev/null
+++ b/tools/kvm/Documentation/kvm-setup.txt
@@ -0,0 +1,15 @@ 
+kvm-setup(1)
+================
+
+NAME
+----
+kvm-setup - Setup a new virtual machine
+
+SYNOPSIS
+--------
+[verse]
+'kvm setup <name>'
+
+DESCRIPTION
+-----------
+The command setups a virtual machine.
diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 669386f..25cbd7e 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -20,6 +20,8 @@  TAGS	:= ctags
 
 PROGRAM	:= kvm
 
+GUEST_INIT := guest/init
+
 OBJS	+= builtin-balloon.o
 OBJS	+= builtin-debug.o
 OBJS	+= builtin-help.o
@@ -28,6 +30,7 @@  OBJS	+= builtin-stat.o
 OBJS	+= builtin-pause.o
 OBJS	+= builtin-resume.o
 OBJS	+= builtin-run.o
+OBJS	+= builtin-setup.o
 OBJS	+= builtin-stop.o
 OBJS	+= builtin-version.o
 OBJS	+= cpuid.o
@@ -159,7 +162,7 @@  WARNINGS += -Wunused-result
 
 CFLAGS	+= $(WARNINGS)
 
-all: $(PROGRAM)
+all: $(PROGRAM) $(GUEST_INIT)
 
 KVMTOOLS-VERSION-FILE:
 	@$(SHELL_PATH) util/KVMTOOLS-VERSION-GEN $(OUTPUT)
@@ -169,6 +172,10 @@  $(PROGRAM): $(DEPS) $(OBJS)
 	$(E) "  LINK    " $@
 	$(Q) $(CC) $(OBJS) $(LIBS) -o $@
 
+$(GUEST_INIT): guest/init.c
+	$(E) "  LINK    " $@
+	$(Q) $(CC) -static guest/init.c -o $@
+
 $(DEPS):
 
 %.d: %.c
@@ -240,7 +247,7 @@  clean:
 	$(Q) rm -f bios/bios-rom.h
 	$(Q) rm -f tests/boot/boot_test.iso
 	$(Q) rm -rf tests/boot/rootfs/
-	$(Q) rm -f $(DEPS) $(OBJS) $(PROGRAM)
+	$(Q) rm -f $(DEPS) $(OBJS) $(PROGRAM) $(GUEST_INIT)
 	$(Q) rm -f cscope.*
 	$(Q) rm -f $(KVM_INCLUDE)/common-cmds.h
 	$(Q) rm -f KVMTOOLS-VERSION-FILE
diff --git a/tools/kvm/builtin-setup.c b/tools/kvm/builtin-setup.c
new file mode 100644
index 0000000..4280fe0
--- /dev/null
+++ b/tools/kvm/builtin-setup.c
@@ -0,0 +1,184 @@ 
+#include <kvm/util.h>
+#include <kvm/kvm-cmd.h>
+#include <kvm/builtin-setup.h>
+#include <kvm/kvm.h>
+#include <kvm/parse-options.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define KVM_PID_FILE_PATH	"/.kvm-tools/"
+#define HOME_DIR		getenv("HOME")
+
+static const char *instance_name;
+
+static const char * const setup_usage[] = {
+	"kvm setup [-n name]",
+	NULL
+};
+
+static const struct option setup_options[] = {
+	OPT_GROUP("General options:"),
+	OPT_STRING('n', "name", &instance_name, "name", "Instance name"),
+	OPT_END()
+};
+
+static void parse_setup_options(int argc, const char **argv)
+{
+	while (argc != 0) {
+		argc = parse_options(argc, argv, setup_options, setup_usage,
+				PARSE_OPT_STOP_AT_NON_OPTION);
+		if (argc != 0)
+			kvm_setup_help();
+	}
+}
+
+void kvm_setup_help(void)
+{
+	usage_with_options(setup_usage, setup_options);
+}
+
+static int copy_file(const char *from, const char *to)
+{
+	int in_fd, out_fd;
+	void *src, *dst;
+	struct stat st;
+	int err = -1;
+
+	in_fd = open(from, O_RDONLY);
+	if (in_fd < 0)
+		return err;
+
+	if (fstat(in_fd, &st) < 0)
+		goto error_close_in;
+
+	out_fd = open(to, O_RDWR | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
+	if (out_fd < 0)
+		goto error_close_in;
+
+	src = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, in_fd, 0);
+	if (src == MAP_FAILED)
+		goto error_close_out;
+
+	if (ftruncate(out_fd, st.st_size) < 0)
+		goto error_munmap_src;
+
+	dst = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, out_fd, 0);
+	if (dst == MAP_FAILED)
+		goto error_munmap_src;
+
+	memcpy(dst, src, st.st_size);
+
+	if (fsync(out_fd) < 0)
+		goto error_munmap_dst;
+
+	err = 0;
+
+error_munmap_dst:
+	munmap(dst, st.st_size);
+error_munmap_src:
+	munmap(src, st.st_size);
+error_close_out:
+	close(out_fd);
+error_close_in:
+	close(in_fd);
+
+	return err;
+}
+
+static const char *guestfs_dirs[] = {
+	"/dev",
+	"/etc",
+	"/home",
+	"/host",
+	"/proc",
+	"/root",
+	"/sys",
+	"/var",
+	"/virt",
+};
+
+static const char *guestfs_symlinks[] = {
+	"/bin",
+	"/lib",
+	"/lib64",
+	"/sbin",
+	"/usr",
+};
+
+static int copy_init(const char *guestfs_name)
+{
+	char path[PATH_MAX];
+
+	snprintf(path, PATH_MAX, "%s%s%s/virt/init", HOME_DIR, KVM_PID_FILE_PATH, guestfs_name);
+
+	return copy_file("guest/init", path);
+}
+
+static int make_guestfs_symlink(const char *guestfs_name, const char *path)
+{
+	char target[PATH_MAX];
+	char name[PATH_MAX];
+
+	snprintf(name, PATH_MAX, "%s%s%s%s", HOME_DIR, KVM_PID_FILE_PATH, guestfs_name, path);
+
+	snprintf(target, PATH_MAX, "/host%s", path);
+
+	return symlink(target, name);
+}
+
+static void make_dir(const char *dir)
+{
+	char name[PATH_MAX];
+
+	snprintf(name, PATH_MAX, "%s%s%s", HOME_DIR, KVM_PID_FILE_PATH, dir);
+
+	mkdir(name, 0777);
+}
+
+static void make_guestfs_dir(const char *guestfs_name, const char *dir)
+{
+	char name[PATH_MAX];
+
+	snprintf(name, PATH_MAX, "%s%s", guestfs_name, dir);
+
+	make_dir(name);
+}
+
+static int do_setup(const char *guestfs_name)
+{
+	unsigned int i;
+
+	make_dir(guestfs_name);
+
+	for (i = 0; i < ARRAY_SIZE(guestfs_dirs); i++)
+		make_guestfs_dir(guestfs_name, guestfs_dirs[i]);
+
+	for (i = 0; i < ARRAY_SIZE(guestfs_symlinks); i++) {
+		make_guestfs_symlink(guestfs_name, guestfs_symlinks[i]);
+	}
+
+	return copy_init(guestfs_name);
+}
+
+int kvm_cmd_setup(int argc, const char **argv, const char *prefix)
+{
+	parse_setup_options(argc, argv);
+
+	if (instance_name == NULL)
+		kvm_setup_help();
+
+	return do_setup(instance_name);
+}
diff --git a/tools/kvm/command-list.txt b/tools/kvm/command-list.txt
index f201d75..0d16c62 100644
--- a/tools/kvm/command-list.txt
+++ b/tools/kvm/command-list.txt
@@ -3,6 +3,7 @@ 
 # command name			category [deprecated] [common]
 #
 kvm-run				mainporcelain common
+kvm-setup			mainporcelain common
 kvm-pause			common
 kvm-resume			common
 kvm-version			common
diff --git a/tools/kvm/guest/init.c b/tools/kvm/guest/init.c
new file mode 100644
index 0000000..837acfb
--- /dev/null
+++ b/tools/kvm/guest/init.c
@@ -0,0 +1,40 @@ 
+/*
+ * This is a simple init for shared rootfs guests. It brings up critical
+ * mountpoints and then launches /bin/sh.
+ */
+#include <sys/mount.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+
+static int run_process(char *filename)
+{
+	char *new_argv[] = { filename, NULL };
+	char *new_env[] = { NULL };
+
+	return execve(filename, new_argv, new_env);
+}
+
+static void do_mounts(void)
+{
+	mount("hostfs", "/host", "9p", MS_RDONLY, "trans=virtio,version=9p2000.L");
+	mount("", "/sys", "sysfs", 0, NULL);
+	mount("proc", "/proc", "proc", 0, NULL);
+	mount("devtmpfs", "/dev", "devtmpfs", 0, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+	puts("Mounting...");
+
+	do_mounts();
+
+	puts("Starting '/bin/sh'...");
+
+	run_process("/bin/sh");
+
+	printf("Init failed: %s\n", strerror(errno));
+
+	return 0;
+}
diff --git a/tools/kvm/include/kvm/builtin-setup.h b/tools/kvm/include/kvm/builtin-setup.h
new file mode 100644
index 0000000..b0eb345
--- /dev/null
+++ b/tools/kvm/include/kvm/builtin-setup.h
@@ -0,0 +1,7 @@ 
+#ifndef KVM__SETUP_H
+#define KVM__SETUP_H
+
+int kvm_cmd_setup(int argc, const char **argv, const char *prefix);
+void kvm_setup_help(void);
+
+#endif
diff --git a/tools/kvm/kvm-cmd.c b/tools/kvm/kvm-cmd.c
index 8fc6d22..96108a8 100644
--- a/tools/kvm/kvm-cmd.c
+++ b/tools/kvm/kvm-cmd.c
@@ -11,6 +11,7 @@ 
 #include "kvm/builtin-balloon.h"
 #include "kvm/builtin-list.h"
 #include "kvm/builtin-version.h"
+#include "kvm/builtin-setup.h"
 #include "kvm/builtin-stop.h"
 #include "kvm/builtin-stat.h"
 #include "kvm/builtin-help.h"
@@ -29,6 +30,7 @@  struct cmd_struct kvm_commands[] = {
 	{ "stop",	kvm_cmd_stop,		kvm_stop_help,		0 },
 	{ "stat",	kvm_cmd_stat,		kvm_stat_help,		0 },
 	{ "help",	kvm_cmd_help,		NULL,			0 },
+	{ "setup",	kvm_cmd_setup,		kvm_setup_help,		0 },
 	{ "run",	kvm_cmd_run,		kvm_run_help,		0 },
 	{ NULL,		NULL,			NULL,			0 },
 };